+++ /dev/null
-module Faraday
- # A Builder that processes requests into responses by passing through an inner
- # middleware stack (heavily inspired by Rack).
- #
- # Faraday::Connection.new(:url => 'http://sushi.com') do |builder|
- # builder.request :url_encoded # Faraday::Request::UrlEncoded
- # builder.adapter :net_http # Faraday::Adapter::NetHttp
- # end
- class RackBuilder
- attr_accessor :handlers
-
- # 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
- @@constants_mutex = Mutex.new
- @@constants = Hash.new { |h, k|
- value = k.respond_to?(:constantize) ? k.constantize : Object.const_get(k)
- @@constants_mutex.synchronize { h[k] = value }
- }
-
- attr_reader :name
-
- def initialize(klass, *args, &block)
- @name = klass.to_s
- if klass.respond_to?(:name)
- @@constants_mutex.synchronize { @@constants[@name] = klass }
- end
- @args, @block = args, block
- end
-
- def klass() @@constants[@name] end
- def inspect() @name end
-
- def ==(other)
- if other.is_a? Handler
- self.name == other.name
- elsif other.respond_to? :name
- klass == other
- else
- @name == other.to_s
- end
- end
-
- def build(app)
- klass.new(app, *@args, &@block)
- end
- end
-
- def initialize(handlers = [])
- @handlers = handlers
- if block_given?
- build(&Proc.new)
- elsif @handlers.empty?
- # default stack, if nothing else is configured
- self.request :url_encoded
- self.adapter Faraday.default_adapter
- end
- end
-
- def build(options = {})
- raise_if_locked
- @handlers.clear unless options[:keep]
- yield(self) if block_given?
- end
-
- def [](idx)
- @handlers[idx]
- 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, &block)
- if klass.is_a? Symbol
- use_symbol(Faraday::Middleware, klass, *args, &block)
- else
- raise_if_locked
- @handlers << self.class::Handler.new(klass, *args, &block)
- end
- end
-
- def request(key, *args, &block)
- use_symbol(Faraday::Request, key, *args, &block)
- end
-
- def response(key, *args, &block)
- use_symbol(Faraday::Response, key, *args, &block)
- end
-
- def adapter(key, *args, &block)
- use_symbol(Faraday::Adapter, key, *args, &block)
- end
-
- ## 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)
- end
-
- alias_method :insert_before, :insert
-
- def insert_after(index, *args, &block)
- index = assert_index(index)
- insert(index + 1, *args, &block)
- 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
-
- # Processes a Request into a Response by passing it through this Builder's
- # middleware stack.
- #
- # connection - Faraday::Connection
- # request - Faraday::Request
- #
- # Returns a Faraday::Response.
- def build_response(connection, request)
- app.call(build_env(connection, request))
- 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
- lock!
- to_app(lambda { |env|
- response = Response.new
- response.finish(env) unless env.parallel?
- env.response = response
- })
- end
- end
-
- def to_app(inner_app)
- # last added handler is the deepest and thus closest to the inner app
- @handlers.reverse.inject(inner_app) { |app, handler| handler.build(app) }
- end
-
- def ==(other)
- other.is_a?(self.class) && @handlers == other.handlers
- end
-
- def dup
- self.class.new(@handlers.dup)
- end
-
- # ENV Keys
- # :method - a symbolized request method (:get, :post)
- # :body - the request body that will eventually be converted to a string.
- # :url - URI instance for the current request.
- # :status - HTTP response status code
- # :request_headers - hash of HTTP Headers to be sent to the server
- # :response_headers - Hash of HTTP headers from the server
- # :parallel_manager - sent if the connection is in parallel mode
- # :request - Hash of options for configuring the request.
- # :timeout - open/read timeout Integer in seconds
- # :open_timeout - read timeout Integer in seconds
- # :proxy - Hash of proxy options
- # :uri - Proxy Server URI
- # :user - Proxy server username
- # :password - Proxy server password
- # :ssl - Hash of options for configuring SSL requests.
- def build_env(connection, request)
- Env.new(request.method, request.body,
- connection.build_exclusive_url(request.path, request.params),
- request.options, request.headers, connection.ssl,
- connection.parallel_manager)
- 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)
- use(mod.lookup_middleware(key), *args, &block)
- end
-
- def assert_index(index)
- idx = index.is_a?(Integer) ? index : @handlers.index(index)
- raise "No such handler: #{index.inspect}" unless idx
- idx
- end
- end
-end