mirror of
https://github.com/lostisland/faraday.git
synced 2025-08-30 00:03:09 -04:00
cache middleware stack instead of rebuilding it on every request
The downside is, middleware can't be modified after making the first request: conn.use MyMiddleware # => OK conn.get('/') conn.use AnotherMiddleware # => raises a Builder::StackLocked error On the plus side, the middleware stack is built only once and then cached as the Connection#app object. The Connection instance can always be "forked off" with the `dup` method and modified as if it were fresh.
This commit is contained in:
parent
9ae912b2c8
commit
b47fb2922f
@ -12,6 +12,9 @@ module Faraday
|
||||
new { |builder| yield builder }
|
||||
end
|
||||
|
||||
# Error raised when trying to modify the stack after calling `lock!`
|
||||
class StackLocked < RuntimeError; end
|
||||
|
||||
# borrowed from ActiveSupport::Dependencies::Reference &
|
||||
# ActionDispatch::MiddlewareStack::Middleware
|
||||
class Handler
|
||||
@ -55,6 +58,7 @@ module Faraday
|
||||
end
|
||||
|
||||
def build(options = {})
|
||||
raise_if_locked
|
||||
@handlers.clear unless options[:keep]
|
||||
yield self if block_given?
|
||||
end
|
||||
@ -76,7 +80,17 @@ module Faraday
|
||||
@handlers.reverse.inject(inner_app) { |app, handler| handler.build(app) }
|
||||
end
|
||||
|
||||
# Locks the middleware stack to ensure no further modifications are possible.
|
||||
def lock!
|
||||
@handlers.freeze
|
||||
end
|
||||
|
||||
def locked?
|
||||
@handlers.frozen?
|
||||
end
|
||||
|
||||
def use(klass, *args)
|
||||
raise_if_locked
|
||||
block = block_given? ? Proc.new : nil
|
||||
@handlers << self.class::Handler.new(klass, *args, &block)
|
||||
end
|
||||
@ -99,6 +113,7 @@ module Faraday
|
||||
## methods to push onto the various positions in the stack:
|
||||
|
||||
def insert(index, *args, &block)
|
||||
raise_if_locked
|
||||
index = assert_index(index)
|
||||
handler = self.class::Handler.new(*args, &block)
|
||||
@handlers.insert(index, handler)
|
||||
@ -112,17 +127,23 @@ module Faraday
|
||||
end
|
||||
|
||||
def swap(index, *args, &block)
|
||||
raise_if_locked
|
||||
index = assert_index(index)
|
||||
@handlers.delete_at(index)
|
||||
insert(index, *args, &block)
|
||||
end
|
||||
|
||||
def delete(handler)
|
||||
raise_if_locked
|
||||
@handlers.delete(handler)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def raise_if_locked
|
||||
raise StackLocked, "can't modify middleware stack after making a request" if locked?
|
||||
end
|
||||
|
||||
def use_symbol(mod, key, *args)
|
||||
block = block_given? ? Proc.new : nil
|
||||
use(mod.lookup_module(key), *args, &block)
|
||||
|
@ -61,6 +61,25 @@ module Faraday
|
||||
@builder.build(options, &block)
|
||||
end
|
||||
|
||||
# The "rack app" wrapped in middleware. All requests are sent here.
|
||||
#
|
||||
# The builder is responsible for creating the app object. After this,
|
||||
# the builder gets locked to ensure no further modifications are made
|
||||
# to the middleware stack.
|
||||
#
|
||||
# Returns an object that responds to `call` and returns a Response.
|
||||
def app
|
||||
@app ||= begin
|
||||
builder.lock!
|
||||
builder.to_app(lambda { |env|
|
||||
# the inner app that creates and returns the Response object
|
||||
response = Response.new
|
||||
response.finish(env) unless env[:parallel_manager]
|
||||
env[:response] = response
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def get(url = nil, headers = nil)
|
||||
block = block_given? ? Proc.new : nil
|
||||
run_request(:get, url, nil, headers, &block)
|
||||
|
@ -77,13 +77,8 @@ module Faraday
|
||||
end
|
||||
|
||||
def run(connection, request_method)
|
||||
app = lambda { |env|
|
||||
response = Response.new
|
||||
response.finish(env) unless env[:parallel_manager]
|
||||
env[:response] = response
|
||||
}
|
||||
env = to_env_hash(connection, request_method)
|
||||
connection.builder.to_app(app).call(env)
|
||||
connection.app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -158,19 +158,19 @@ else
|
||||
|
||||
def create_connection(adapter)
|
||||
if adapter == :default
|
||||
Faraday.default_connection.tap do |conn|
|
||||
conn.url_prefix = LIVE_SERVER
|
||||
conn.headers['X-Faraday-Adapter'] = adapter.to_s
|
||||
end
|
||||
builder_block = nil
|
||||
else
|
||||
Faraday::Connection.new LIVE_SERVER, :headers => {'X-Faraday-Adapter' => adapter.to_s} do |b|
|
||||
builder_block = Proc.new do |b|
|
||||
b.request :multipart
|
||||
b.request :url_encoded
|
||||
b.use adapter
|
||||
end
|
||||
end.tap do |conn|
|
||||
target = conn.builder.handlers.last
|
||||
conn.builder.insert_before target, Faraday::Response::RaiseError
|
||||
end
|
||||
|
||||
Faraday::Connection.new(LIVE_SERVER, &builder_block).tap do |conn|
|
||||
conn.headers['X-Faraday-Adapter'] = adapter.to_s
|
||||
adapter_handler = conn.builder.handlers.last
|
||||
conn.builder.insert_before adapter_handler, Faraday::Response::RaiseError
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,8 +26,6 @@ class MiddlewareStackTest < Faraday::TestCase
|
||||
|
||||
def test_allows_rebuilding
|
||||
build_stack Apple
|
||||
assert_handlers %w[Apple]
|
||||
|
||||
build_stack Orange
|
||||
assert_handlers %w[Orange]
|
||||
end
|
||||
@ -67,6 +65,28 @@ class MiddlewareStackTest < Faraday::TestCase
|
||||
assert_handlers %w[Orange]
|
||||
end
|
||||
|
||||
def test_stack_is_locked_after_making_requests
|
||||
build_stack Apple
|
||||
assert !@builder.locked?
|
||||
@conn.get('/')
|
||||
assert @builder.locked?
|
||||
|
||||
assert_raises Faraday::Builder::StackLocked do
|
||||
@conn.use Orange
|
||||
end
|
||||
end
|
||||
|
||||
def test_duped_stack_is_unlocked
|
||||
build_stack Apple
|
||||
assert !@builder.locked?
|
||||
@builder.lock!
|
||||
assert @builder.locked?
|
||||
|
||||
duped_connection = @conn.dup
|
||||
assert_equal @builder, duped_connection.builder
|
||||
assert !duped_connection.builder.locked?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# make a stack with test adapter that reflects the order of middleware
|
||||
|
Loading…
x
Reference in New Issue
Block a user