require 'addressable/uri' require 'set' require 'base64' module Faraday class Connection include Addressable, Faraday::Utils METHODS = Set.new [:get, :post, :put, :delete, :head] METHODS_WITH_BODIES = Set.new [:post, :put] attr_accessor :host, :port, :scheme, :params, :headers, :parallel_manager attr_reader :path_prefix, :builder, :options, :ssl # :url # :params # :headers # :request # :ssl def initialize(url = nil, options = {}) if url.is_a?(Hash) options = url url = options[:url] end @headers = HeaderHash.new @params = {} @options = options[:request] || {} @ssl = options[:ssl] || {} @parallel_manager = options[:parallel] self.url_prefix = url if url proxy(options[:proxy]) merge_params @params, options[:params] if options[:params] merge_headers @headers, options[:headers] if options[:headers] if block_given? block = Proc.new @builder = Builder.new @builder.build { block.call(self) } else @builder = options[:builder] || Builder.new end end def use(klass, *args, &block) @builder.use(klass, *args, &block) end def request(key, *args, &block) @builder.request(key, *args, &block) end def response(key, *args, &block) @builder.response(key, *args, &block) end def adapter(key, *args, &block) @builder.adapter(key, *args, &block) end def build(options = {}, &block) @builder.build(options, &block) end def get(url = nil, headers = nil) block = block_given? ? Proc.new : nil run_request(:get, url, nil, headers, &block) end def post(url = nil, body = nil, headers = nil) block = block_given? ? Proc.new : nil run_request(:post, url, body, headers, &block) end def put(url = nil, body = nil, headers = nil) block = block_given? ? Proc.new : nil run_request(:put, url, body, headers, &block) end def head(url = nil, headers = nil) block = block_given? ? Proc.new : nil run_request(:head, url, nil, headers, &block) end def delete(url = nil, headers = nil) block = block_given? ? Proc.new : nil run_request(:delete, url, nil, headers, &block) end def basic_auth(login, pass) auth = Base64.encode64("#{login}:#{pass}") auth.gsub!("\n", "") @headers['authorization'] = "Basic #{auth}" end def token_auth(token, options = {}) values = ["token=#{token.to_s.inspect}"] options.each do |key, value| values << "#{key}=#{value.to_s.inspect}" end # 21 = "Authorization: Token ".size comma = ",\n#{' ' * 21}" @headers['authorization'] = "Token #{values * comma}" end def in_parallel? !!@parallel_manager end def in_parallel(manager) @parallel_manager = manager yield @parallel_manager && @parallel_manager.run ensure @parallel_manager = nil end def proxy(arg = nil) return @proxy if arg.nil? @proxy = case arg when String then {:uri => proxy_arg_to_uri(arg)} when URI then {:uri => arg} when Hash then arg if arg[:uri] = proxy_arg_to_uri(arg[:uri]) arg else raise ArgumentError, "no :uri option." end end end # Parses the giving url with Addressable::URI and stores the individual # components in this connection. These components serve as defaults for # requests made by this connection. # # conn = Faraday::Connection.new { ... } # conn.url_prefix = "https://sushi.com/api" # conn.scheme # => https # conn.path_prefix # => "/api" # # conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri # def url_prefix=(url) uri = URI.parse(url) self.scheme = uri.scheme self.host = uri.host self.port = uri.port self.path_prefix = uri.path if uri.query && !uri.query.empty? merge_params @params, parse_query(uri.query) end if uri.user && uri.password basic_auth(uri.user, uri.password) end end # Ensures that the path prefix always has a leading / and no trailing / def path_prefix=(value) if value value.chomp! "/" value.replace "/#{value}" if value !~ /^\// end @path_prefix = value end # return the assembled Rack application for this instance. def to_app @builder.to_app end def run_request(method, url, body, headers) if !METHODS.include?(method) raise ArgumentError, "unknown http method: #{method}" end Request.run(self, method) do |req| req.url(url) if url req.headers.update(headers) if headers req.body = body if body yield req if block_given? end end # Takes a relative url for a request and combines it with the defaults # set on the connection instance. # # conn = Faraday::Connection.new { ... } # conn.url_prefix = "https://sushi.com/api?token=abc" # conn.scheme # => https # conn.path_prefix # => "/api" # # conn.build_url("nigiri?page=2") # => https://sushi.com/api/nigiri?token=abc&page=2 # conn.build_url("nigiri", :page => 2) # => https://sushi.com/api/nigiri?token=abc&page=2 # def build_url(url, params = nil) uri = URI.parse(url.to_s) if @path_prefix && uri.path !~ /^\// uri.path = "#{@path_prefix.size > 1 ? @path_prefix : nil}/#{uri.path}" end uri.host ||= @host uri.port ||= @port uri.scheme ||= @scheme replace_query(uri, params) uri end def dup self.class.new(build_url(''), :headers => headers.dup, :params => params.dup, :builder => builder.dup) end def replace_query(uri, params) url_params = @params.dup if uri.query && !uri.query.empty? merge_params(url_params, parse_query(uri.query)) end if params && !params.empty? merge_params(url_params, params) end uri.query = url_params.empty? ? nil : build_query(url_params) uri end def proxy_arg_to_uri(arg) case arg when String then URI.parse(arg) when URI then arg end end end end