diff --git a/context/middleware.md b/context/middleware.md index 018bd83c..5ccade40 100644 --- a/context/middleware.md +++ b/context/middleware.md @@ -29,7 +29,11 @@ use Utopia::Redirection::Rewrite, use Utopia::Redirection::DirectoryIndex, index: 'index.html' -# Redirect (error) status codes to actual pages: +~~~ + +Place {ruby Utopia::Redirection::Errors} after all client-visible redirection middleware. It maps unhandled error responses to internal error documents while retaining the original response status. Because its internal requests invoke the downstream application directly, redirects configured before it are bypassed: + +~~~ ruby use Utopia::Redirection::Errors, 404 => '/errors/file-not-found' ~~~ diff --git a/guides/middleware/readme.md b/guides/middleware/readme.md index 40152254..e0bb4675 100644 --- a/guides/middleware/readme.md +++ b/guides/middleware/readme.md @@ -29,7 +29,11 @@ use Utopia::Redirection::Rewrite, use Utopia::Redirection::DirectoryIndex, index: 'index.html' -# Redirect (error) status codes to actual pages: +~~~ + +Place {ruby Utopia::Redirection::Errors} after all client-visible redirection middleware. It maps unhandled error responses to internal error documents while retaining the original response status. Because its internal requests invoke the downstream application directly, redirects configured before it are bypassed: + +~~~ ruby use Utopia::Redirection::Errors, 404 => '/errors/file-not-found' ~~~ diff --git a/lib/utopia/redirection/errors.rb b/lib/utopia/redirection/errors.rb index 7132aa9f..8331c218 100644 --- a/lib/utopia/redirection/errors.rb +++ b/lib/utopia/redirection/errors.rb @@ -11,6 +11,10 @@ module Utopia module Redirection # A middleware which performs internal redirects based on error status codes. + # + # Place this middleware after client-visible redirection middleware in the + # application configuration. Internal error-document requests invoke the + # delegate directly, bypassing middleware configured before this one. class Errors < Protocol::HTTP::Middleware # @param codes [Hash] The redirection path for a given error code. def initialize(app, codes = {}) diff --git a/setup/site/config/application.rb b/setup/site/config/application.rb index a520688c..a3c9c799 100644 --- a/setup/site/config/application.rb +++ b/setup/site/config/application.rb @@ -31,6 +31,7 @@ use Utopia::Redirection::DirectoryIndex + # Handle error documents after client redirects so internal requests bypass them: use Utopia::Redirection::Errors, { 404 => "/errors/file-not-found" } diff --git a/test/utopia/redirection.rb b/test/utopia/redirection.rb index 1076e9f3..cf320ce2 100644 --- a/test/utopia/redirection.rb +++ b/test/utopia/redirection.rb @@ -34,13 +34,13 @@ def tracked_body(name, events) }) do use Utopia::Redirection::Rewrite, {"/" => "/welcome/index"} use Utopia::Redirection::DirectoryIndex + use Utopia::Redirection::Moved, "/a", "/b" + use Utopia::Redirection::Moved, "/hierarchy/", "/hierarchy", flatten: true + use Utopia::Redirection::Moved, "/weird", "/status", status: 333 use Utopia::Redirection::Errors, { 404 => "/error", 418 => "/teapot" } - use Utopia::Redirection::Moved, "/a", "/b" - use Utopia::Redirection::Moved, "/hierarchy/", "/hierarchy", flatten: true - use Utopia::Redirection::Moved, "/weird", "/status", status: 333 end end @@ -84,6 +84,25 @@ def tracked_body(name, events) expect(last_response.read).to be == "File not found :(" end + it "bypasses client redirects for internal error documents" do + application = Utopia::Application.build(Protocol::HTTP::Middleware.for do |request| + if request.path_info == "/error" + Utopia::Response.text("Internal error document") + else + Utopia::Response[404, {}, []] + end + end) do + use Utopia::Redirection::Rewrite, {"/error" => "/redirected"} + use Utopia::Redirection::Errors, 404 => "/error" + end + + response = application.call(Protocol::HTTP::Request["GET", "/missing"]) + + expect(response.status).to be == 404 + expect(response.headers["location"]).to be == nil + expect(response.read).to be == "Internal error document" + end + it "closes the response replaced by an error document" do events = [] application = Utopia::Application.build(Protocol::HTTP::Middleware.for do |request|