1
0
Fork 0
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:
Ryan Tomayko 2009-02-22 00:53:43 -08:00
parent 6fd8aaadd1
commit cf32f2e680
3 changed files with 122 additions and 33 deletions

View file

@ -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;

View file

@ -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)

View file

@ -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