mirror of
https://github.com/sinatra/sinatra
synced 2023-03-27 23:18:01 -04:00
Middleware enhancements: automatic and explicit forward to downstream [#126]
This commit is contained in:
parent
6fd8aaadd1
commit
cf32f2e680
3 changed files with 122 additions and 33 deletions
6
CHANGES
6
CHANGES
|
@ -5,6 +5,12 @@
|
|||
passed as arguments to the block. [#140]
|
||||
* The "helpers" method now takes a variable number of modules
|
||||
along with the normal block syntax. [#133]
|
||||
* New request-level #forward method for middleware components: passes
|
||||
the env to the downstream app and merges the response status, headers,
|
||||
and body into the current context.
|
||||
* Requests are now automatically forwarded to the downstream app when
|
||||
running as middleware and no matching route is found or all routes
|
||||
pass.
|
||||
* New simple API for extensions/plugins to add DSL-level and
|
||||
request-level methods. Use Sinatra.register(mixin) to extend
|
||||
the DSL with all public methods defined in the mixin module;
|
||||
|
|
|
@ -366,6 +366,16 @@ module Sinatra
|
|||
throw :pass
|
||||
end
|
||||
|
||||
# Forward the request to the downstream app -- middleware only.
|
||||
def forward
|
||||
fail "downstream app not set" unless @app.respond_to? :call
|
||||
status, headers, body = @app.call(@request.env)
|
||||
@response.status = status
|
||||
@response.body = body
|
||||
headers.each { |k, v| @response[k] = v }
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
# Run before filters and then locate and run a matching route.
|
||||
def route!
|
||||
|
@ -409,7 +419,13 @@ module Sinatra
|
|||
end
|
||||
end
|
||||
|
||||
raise NotFound
|
||||
# No matching route found or all routes passed -- forward downstream
|
||||
# when running as middleware; 404 when running as normal app.
|
||||
if @app
|
||||
forward
|
||||
else
|
||||
raise NotFound
|
||||
end
|
||||
end
|
||||
|
||||
def nested_params(params)
|
||||
|
|
|
@ -1,45 +1,112 @@
|
|||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
describe 'Sinatra::Base' do
|
||||
it 'includes Rack::Utils' do
|
||||
assert Sinatra::Base.included_modules.include?(Rack::Utils)
|
||||
describe 'Sinatra::Base subclasses' do
|
||||
|
||||
class TestApp < Sinatra::Base
|
||||
get '/' do
|
||||
'Hello World'
|
||||
end
|
||||
end
|
||||
|
||||
it 'can be used as a Rack application' do
|
||||
mock_app {
|
||||
get '/' do
|
||||
'Hello World'
|
||||
end
|
||||
}
|
||||
assert @app.respond_to?(:call)
|
||||
it 'include Rack::Utils' do
|
||||
assert TestApp.included_modules.include?(Rack::Utils)
|
||||
end
|
||||
|
||||
request = Rack::MockRequest.new(@app)
|
||||
it 'processes requests with #call' do
|
||||
assert TestApp.respond_to?(:call)
|
||||
|
||||
request = Rack::MockRequest.new(TestApp)
|
||||
response = request.get('/')
|
||||
assert response.ok?
|
||||
assert_equal 'Hello World', response.body
|
||||
end
|
||||
|
||||
it 'can be used as Rack middleware' do
|
||||
app = lambda { |env| [200, {}, ['Goodbye World']] }
|
||||
mock_middleware =
|
||||
mock_app {
|
||||
get '/' do
|
||||
'Hello World'
|
||||
end
|
||||
get '/goodbye' do
|
||||
@app.call(request.env)
|
||||
end
|
||||
}
|
||||
middleware = mock_middleware.new(app)
|
||||
assert_same app, middleware.app
|
||||
class TestApp < Sinatra::Base
|
||||
get '/state' do
|
||||
body = "Foo: #{@foo}"
|
||||
@foo = 'discard'
|
||||
body
|
||||
end
|
||||
end
|
||||
|
||||
request = Rack::MockRequest.new(middleware)
|
||||
response = request.get('/')
|
||||
assert response.ok?
|
||||
assert_equal 'Hello World', response.body
|
||||
|
||||
response = request.get('/goodbye')
|
||||
assert response.ok?
|
||||
assert_equal 'Goodbye World', response.body
|
||||
it 'does not maintain state between requests' do
|
||||
request = Rack::MockRequest.new(TestApp)
|
||||
2.times do
|
||||
response = request.get('/state')
|
||||
assert response.ok?
|
||||
assert_equal 'Foo: ', response.body
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Sinatra::Base as Rack middleware" do
|
||||
|
||||
app = lambda { |env|
|
||||
[210, {'X-Downstream' => 'true'}, ['Hello from downstream']] }
|
||||
|
||||
class TestMiddleware < Sinatra::Base
|
||||
end
|
||||
|
||||
it 'creates a middleware that responds to #call with .new' do
|
||||
middleware = TestMiddleware.new(app)
|
||||
assert middleware.respond_to?(:call)
|
||||
end
|
||||
|
||||
it 'exposes the downstream app' do
|
||||
middleware = TestMiddleware.new(app)
|
||||
assert_same app, middleware.app
|
||||
end
|
||||
|
||||
class TestMiddleware < Sinatra::Base
|
||||
get '/' do
|
||||
'Hello from middleware'
|
||||
end
|
||||
end
|
||||
|
||||
middleware = TestMiddleware.new(app)
|
||||
request = Rack::MockRequest.new(middleware)
|
||||
|
||||
it 'intercepts requests' do
|
||||
response = request.get('/')
|
||||
assert response.ok?
|
||||
assert_equal 'Hello from middleware', response.body
|
||||
end
|
||||
|
||||
it 'automatically forwards requests downstream when no matching route found' do
|
||||
response = request.get('/missing')
|
||||
assert_equal 210, response.status
|
||||
assert_equal 'Hello from downstream', response.body
|
||||
end
|
||||
|
||||
class TestMiddleware < Sinatra::Base
|
||||
get '/low-level-forward' do
|
||||
app.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
it 'can call the downstream app directly and return result' do
|
||||
response = request.get('/low-level-forward')
|
||||
assert_equal 210, response.status
|
||||
assert_equal 'true', response['X-Downstream']
|
||||
assert_equal 'Hello from downstream', response.body
|
||||
end
|
||||
|
||||
class TestMiddleware < Sinatra::Base
|
||||
get '/explicit-forward' do
|
||||
response['X-Middleware'] = 'true'
|
||||
res = forward
|
||||
assert_nil res
|
||||
assert_equal 210, response.status
|
||||
assert_equal 'true', response['X-Downstream']
|
||||
assert_equal ['Hello from downstream'], response.body
|
||||
'Hello after explicit forward'
|
||||
end
|
||||
end
|
||||
|
||||
it 'forwards the request downstream and integrates the response into the current context' do
|
||||
response = request.get('/explicit-forward')
|
||||
assert_equal 210, response.status
|
||||
assert_equal 'true', response['X-Downstream']
|
||||
assert_equal 'Hello after explicit forward', response.body
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue