diff options
author | Jeremy Evans <code@jeremyevans.net> | 2020-02-04 10:15:20 -0800 |
---|---|---|
committer | Jeremy Evans <code@jeremyevans.net> | 2020-02-04 10:36:40 -0800 |
commit | 274336e96bc6034ee1e56bf9c9fd7f18874483e9 (patch) | |
tree | 0c572107dd7b1ea328d2310c230a91d231c05012 | |
parent | dbd33f7e8c2149332636e32301c295197f046490 (diff) | |
download | rack-274336e96bc6034ee1e56bf9c9fd7f18874483e9.tar.gz |
Fix Cascade to use the last response if all responses should be cascaded
This was broken in the last commit for Cascade. The behavior
wasn't specified, but it previously returned the response from
the final app, and that is a better idea than the default 404
response.
Also add documentation for Cascade.
-rw-r--r-- | lib/rack/cascade.rb | 30 | ||||
-rw-r--r-- | test/spec_cascade.rb | 6 |
2 files changed, 29 insertions, 7 deletions
diff --git a/lib/rack/cascade.rb b/lib/rack/cascade.rb index 8a3cf66b..d71274c2 100644 --- a/lib/rack/cascade.rb +++ b/lib/rack/cascade.rb @@ -2,24 +2,37 @@ module Rack # Rack::Cascade tries a request on several apps, and returns the - # first response that is not 404 or 405 (or in a list of configurable - # status codes). + # first response that is not 404 or 405 (or in a list of configured + # status codes). If all applications tried return one of the configured + # status codes, return the last response. class Cascade # deprecated, no longer used NotFound = [404, { CONTENT_TYPE => "text/plain" }, []] + # An array of applications to try in order. attr_reader :apps - def initialize(apps, catch = [404, 405]) + # Set the apps to send requests to, and what statuses result in + # cascading. Arguments: + # + # apps: An enumerable of rack applications. + # cascade_for: The statuses to use cascading for. If a response is received + # from an app, the next app is tried. + def initialize(apps, cascade_for = [404, 405]) @apps = [] apps.each { |app| add app } - @catch = {} - [*catch].each { |status| @catch[status] = true } + @cascade_for = {} + [*cascade_for].each { |status| @cascade_for[status] = true } end + # Call each app in order. If the responses uses a status that requires + # cascading, try the next app. If all responses require cascading, + # return the response from the last app. def call(env) + return [404, { CONTENT_TYPE => "text/plain" }, []] if @apps.empty? + result = nil last_body = nil @apps.each do |app| @@ -32,17 +45,20 @@ module Rack last_body.close if last_body.respond_to? :close result = app.call(env) + return result unless @cascade_for.include?(result[0].to_i) last_body = result[2] - return result unless @catch.include?(result[0].to_i) end - [404, { CONTENT_TYPE => "text/plain" }, []] + result end + # Append an app to the list of apps to cascade. This app will + # be tried last. def add(app) @apps << app end + # Whether the given app is one of the apps to cascade to. def include?(app) @apps.include?(app) end diff --git a/test/spec_cascade.rb b/test/spec_cascade.rb index 883b47c5..8f1fd131 100644 --- a/test/spec_cascade.rb +++ b/test/spec_cascade.rb @@ -61,6 +61,12 @@ describe Rack::Cascade do body.must_be_empty end + it "returns final response if all responses are cascaded" do + app = Rack::Cascade.new([]) + app << lambda { |env| [405, {}, []] } + app.call({})[0].must_equal 405 + end + it "append new app" do cascade = Rack::Cascade.new([], [404, 403]) Rack::MockRequest.new(cascade).get('/').must_be :not_found? |