pax_global_header00006660000000000000000000000064126047734470014530gustar00rootroot0000000000000052 comment=f97f1874e4cd976f564870bb6151fb05e5f4c8b7 faraday-0.9.2/000077500000000000000000000000001260477344700131475ustar00rootroot00000000000000faraday-0.9.2/.document000066400000000000000000000001041260477344700147610ustar00rootroot00000000000000CONTRIBUTING.md LICENSE.md README.md bin/* lib/**/*.rb test/**/*.rb faraday-0.9.2/.gitignore000066400000000000000000000002351260477344700151370ustar00rootroot00000000000000## PROJECT::GENERAL coverage rdoc doc log pkg/* tmp .rvmrc .ruby-version ## BUNDLER bin *.gem .bundle Gemfile.lock vendor/bundle ## PROJECT::SPECIFIC .rbx faraday-0.9.2/.travis.yml000066400000000000000000000012031260477344700152540ustar00rootroot00000000000000sudo: false language: ruby script: bundle exec script/test cache: bundler rvm: - 1.8.7 - 1.9.2 - 1.9.3 - 2.0.0 - 2.1 - 2.2 - jruby-18mode - jruby-19mode - jruby-20mode - jruby-21mode - jruby-head - rbx-2 matrix: allow_failures: # "A fatal error has been detected by the Java Runtime Environment: # Internal Error (sharedRuntime.cpp:843)" - rvm: jruby-18mode - rvm: jruby-19mode - rvm: jruby-20mode - rvm: jruby-21mode - rvm: jruby-head # random crashes - rvm: rbx-2 fast_finish: true env: matrix: - SSL=no - SSL=yes global: - JRUBY_OPTS="$JRUBY_OPTS --debug" faraday-0.9.2/CHANGELOG.md000066400000000000000000000016601260477344700147630ustar00rootroot00000000000000# Faraday Changelog ## v0.9.1 * Refactor Net:HTTP adapter so that with_net_http_connection can be overridden to allow pooled connections. (@Ben-M) * Add configurable methods that bypass `retry_if` in the Retry request middleware. (@mike-bourgeous) ## v0.9.0 * Add HTTPClient adapter (@hakanensari) * Improve Retry handler (@mislav) * Remove autoloading by default (@technoweenie) * Improve internal docs (@technoweenie, @mislav) * Respect user/password in http proxy string (@mislav) * Adapter options are structs. Reinforces consistent options across adapters (@technoweenie) * Stop stripping trailing / off base URLs in a Faraday::Connection. (@technoweenie) * Add a configurable URI parser. (@technoweenie) * Remove need to manually autoload when using the authorization header helpers on `Faraday::Connection`. (@technoweenie) * `Faraday::Adapter::Test` respects the `Faraday::RequestOptions#params_encoder` option. (@technoweenie) faraday-0.9.2/CONTRIBUTING.md000066400000000000000000000021271260477344700154020ustar00rootroot00000000000000## Contributing You can run the test suite against a live server by running `script/test`. It automatically starts a test server in background. Only tests in `test/adapters/*_test.rb` require a server, though. ``` sh # run the whole suite $ script/test # run only specific files $ script/test excon typhoeus # run tests using SSL $ SSL=yes script/test ``` We will accept middleware that: 1. is useful to a broader audience, but can be implemented relatively simple; and 2. which isn't already present in [faraday_middleware][] project. We will accept adapters that: 1. support SSL & streaming; 1. are proven and may have better performance than existing ones; or 2. if they have features not present in included adapters. We are pushing towards a 1.0 release, when we will have to follow [Semantic Versioning][semver]. If your patch includes changes to break compatibility, note that so we can add it to the [Changelog][]. [semver]: http://semver.org/ [changelog]: https://github.com/lostisland/faraday/wiki/Changelog [faraday_middleware]: https://github.com/lostisland/faraday_middleware/wiki faraday-0.9.2/Gemfile000066400000000000000000000015111260477344700144400ustar00rootroot00000000000000source 'https://rubygems.org' gem 'ffi-ncurses', '~> 0.3', :platforms => :jruby gem 'jruby-openssl', '~> 0.8.8', :platforms => :jruby gem 'rake' group :test do gem 'coveralls', :require => false gem 'em-http-request', '>= 1.1', :require => 'em-http' gem 'em-synchrony', '>= 1.0.3', :require => ['em-synchrony', 'em-synchrony/em-http'] gem 'excon', '>= 0.27.4' gem 'httpclient', '>= 2.2' gem 'mime-types', '~> 1.25', :platforms => [:jruby, :ruby_18] gem 'minitest', '>= 5.0.5' gem 'net-http-persistent', '>= 2.9.4' gem 'patron', '>= 0.4.2', :platforms => :ruby gem 'rack-test', '>= 0.6', :require => 'rack/test' gem 'rest-client', '~> 1.6.0', :platforms => [:jruby, :ruby_18] gem 'simplecov' gem 'sinatra', '~> 1.3' gem 'typhoeus', '~> 0.3.3', :platforms => [:ruby_18, :ruby_19, :ruby_20, :ruby_21] end gemspec faraday-0.9.2/LICENSE.md000066400000000000000000000020601260477344700145510ustar00rootroot00000000000000Copyright (c) 2009-2015 Rick Olson, Zack Hobson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. faraday-0.9.2/README.md000066400000000000000000000167171260477344700144420ustar00rootroot00000000000000# Faraday Faraday is an HTTP client lib that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle. Faraday supports these adapters: * [Net::HTTP][net_http] _(default)_ * [Net::HTTP::Persistent][persistent] * [Excon][] * [Typhoeus][] * [Patron][] * [EventMachine][] * [HTTPClient][] It also includes a Rack adapter for hitting loaded Rack applications through Rack::Test, and a Test adapter for stubbing requests by hand. ## Usage ```ruby conn = Faraday.new(:url => 'http://sushi.com') do |faraday| faraday.request :url_encoded # form-encode POST params faraday.response :logger # log requests to STDOUT faraday.adapter Faraday.default_adapter # make requests with Net::HTTP end ## GET ## response = conn.get '/nigiri/sake.json' # GET http://sushi.com/nigiri/sake.json response.body conn.get '/nigiri', { :name => 'Maguro' } # GET http://sushi.com/nigiri?name=Maguro conn.get do |req| # GET http://sushi.com/search?page=2&limit=100 req.url '/search', :page => 2 req.params['limit'] = 100 end ## POST ## conn.post '/nigiri', { :name => 'Maguro' } # POST "name=maguro" to http://sushi.com/nigiri # post payload as JSON instead of "www-form-urlencoded" encoding: conn.post do |req| req.url '/nigiri' req.headers['Content-Type'] = 'application/json' req.body = '{ "name": "Unagi" }' end ## Per-request options ## conn.get do |req| req.url '/search' req.options.timeout = 5 # open/read timeout in seconds req.options.open_timeout = 2 # connection open timeout in seconds end ``` If you don't need to set up anything, you can roll with just the default middleware stack and default adapter (see [Faraday::RackBuilder#initialize](https://github.com/lostisland/faraday/blob/master/lib/faraday/rack_builder.rb)): ```ruby response = Faraday.get 'http://sushi.com/nigiri/sake.json' ``` ### Changing how parameters are serialized Sometimes you need to send the same URL parameter multiple times with different values. This requires manually setting the parameter encoder and can be done on either per-connection or per-request basis. ```ruby # per-connection setting conn = Faraday.new :params_encoder => Faraday::FlatParamsEncoder conn.get do |req| # per-request setting: # req.options.params_encoder = my_encoder req.params['roll'] = ['california', 'philadelphia'] end # GET 'http://sushi.com?roll=california&roll=philadelphia' ``` The value of Faraday `params_encoder` can be any object that responds to: * `encode(hash) #=> String` * `decode(string) #=> Hash` The encoder will affect both how query strings are processed and how POST bodies get serialized. The default encoder is Faraday::NestedParamsEncoder. ## Advanced middleware usage The order in which middleware is stacked is important. Like with Rack, the first middleware on the list wraps all others, while the last middleware is the innermost one, so that must be the adapter. ```ruby Faraday.new(...) do |conn| # POST/PUT params encoders: conn.request :multipart conn.request :url_encoded conn.adapter :net_http end ``` This request middleware setup affects POST/PUT requests in the following way: 1. `Request::Multipart` checks for files in the payload, otherwise leaves everything untouched; 2. `Request::UrlEncoded` encodes as "application/x-www-form-urlencoded" if not already encoded or of another type Swapping middleware means giving the other priority. Specifying the "Content-Type" for the request is explicitly stating which middleware should process it. Examples: ```ruby # uploading a file: payload[:profile_pic] = Faraday::UploadIO.new('/path/to/avatar.jpg', 'image/jpeg') # "Multipart" middleware detects files and encodes with "multipart/form-data": conn.put '/profile', payload ``` ## Writing middleware Middleware are classes that implement a `call` instance method. They hook into the request/response cycle. ```ruby def call(request_env) # do something with the request # request_env[:request_headers].merge!(...) @app.call(request_env).on_complete do |response_env| # do something with the response # response_env[:response_headers].merge!(...) end end ``` It's important to do all processing of the response only in the `on_complete` block. This enables middleware to work in parallel mode where requests are asynchronous. The `env` is a hash with symbol keys that contains info about the request and, later, response. Some keys are: ``` # request phase :method - :get, :post, ... :url - URI for the current request; also contains GET parameters :body - POST parameters for :post/:put requests :request_headers # response phase :status - HTTP response status code, such as 200 :body - the response body :response_headers ``` ## Using Faraday for testing ```ruby # It's possible to define stubbed request outside a test adapter block. stubs = Faraday::Adapter::Test::Stubs.new do |stub| stub.get('/tamago') { |env| [200, {}, 'egg'] } end # You can pass stubbed request to the test adapter or define them in a block # or a combination of the two. test = Faraday.new do |builder| builder.adapter :test, stubs do |stub| stub.get('/ebi') { |env| [ 200, {}, 'shrimp' ]} end end # It's also possible to stub additional requests after the connection has # been initialized. This is useful for testing. stubs.get('/uni') { |env| [ 200, {}, 'urchin' ]} resp = test.get '/tamago' resp.body # => 'egg' resp = test.get '/ebi' resp.body # => 'shrimp' resp = test.get '/uni' resp.body # => 'urchin' resp = test.get '/else' #=> raises "no such stub" error # If you like, you can treat your stubs as mocks by verifying that all of # the stubbed calls were made. NOTE that this feature is still fairly # experimental: It will not verify the order or count of any stub, only that # it was called once during the course of the test. stubs.verify_stubbed_calls ``` ## TODO * support streaming requests/responses * better stubbing API ## Supported Ruby versions This library aims to support and is [tested against][travis] the following Ruby implementations: * Ruby 1.8.7+ * [JRuby][] 1.7+ * [Rubinius][] 2+ If something doesn't work on one of these Ruby versions, it's a bug. This library may inadvertently work (or seem to work) on other Ruby implementations, however support will only be provided for the versions listed above. If you would like this library to support another Ruby version, you may volunteer to be a maintainer. Being a maintainer entails making sure all tests run and pass on that implementation. When something breaks on your implementation, you will be responsible for providing patches in a timely fashion. If critical issues for a particular implementation exist at the time of a major release, support for that Ruby version may be dropped. ## Copyright Copyright (c) 2009-2013 [Rick Olson](mailto:technoweenie@gmail.com), Zack Hobson. See [LICENSE][] for details. [net_http]: http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/Net/HTTP.html [persistent]: https://github.com/drbrain/net-http-persistent [travis]: http://travis-ci.org/lostisland/faraday [excon]: https://github.com/geemus/excon#readme [typhoeus]: https://github.com/typhoeus/typhoeus#readme [patron]: http://toland.github.com/patron/ [eventmachine]: https://github.com/igrigorik/em-http-request#readme [httpclient]: https://github.com/nahi/httpclient [jruby]: http://jruby.org/ [rubinius]: http://rubini.us/ [license]: LICENSE.md faraday-0.9.2/Rakefile000066400000000000000000000001551260477344700146150ustar00rootroot00000000000000require 'rake/testtask' task :default => :test desc "Run all tests" task :test do exec 'script/test' end faraday-0.9.2/faraday.gemspec000066400000000000000000000010651260477344700161250ustar00rootroot00000000000000lib = "faraday" lib_file = File.expand_path("../lib/#{lib}.rb", __FILE__) File.read(lib_file) =~ /\bVERSION\s*=\s*["'](.+?)["']/ version = $1 Gem::Specification.new do |spec| spec.name = lib spec.version = version spec.summary = "HTTP/REST API client library." spec.authors = ["Rick Olson"] spec.email = 'technoweenie@gmail.com' spec.homepage = 'https://github.com/lostisland/faraday' spec.licenses = ['MIT'] spec.add_dependency 'multipart-post', '>= 1.2', '< 3' spec.files = `git ls-files -z lib LICENSE.md README.md`.split("\0") end faraday-0.9.2/lib/000077500000000000000000000000001260477344700137155ustar00rootroot00000000000000faraday-0.9.2/lib/faraday.rb000066400000000000000000000174121260477344700156560ustar00rootroot00000000000000require 'thread' require 'cgi' require 'set' require 'forwardable' # Public: This is the main namespace for Faraday. You can either use it to # create Faraday::Connection objects, or access it directly. # # Examples # # Faraday.get "http://faraday.com" # # conn = Faraday.new "http://faraday.com" # conn.get '/' # module Faraday VERSION = "0.9.2" class << self # Public: Gets or sets the root path that Faraday is being loaded from. # This is the root from where the libraries are auto-loaded from. attr_accessor :root_path # Public: Gets or sets the path that the Faraday libs are loaded from. attr_accessor :lib_path # Public: Gets or sets the Symbol key identifying a default Adapter to use # for the default Faraday::Connection. attr_reader :default_adapter # Public: Sets the default Faraday::Connection for simple scripts that # access the Faraday constant directly. # # Faraday.get "https://faraday.com" attr_writer :default_connection # Public: Sets the default options used when calling Faraday#new. attr_writer :default_connection_options # Public: Initializes a new Faraday::Connection. # # url - The optional String base URL to use as a prefix for all # requests. Can also be the options Hash. # options - The optional Hash used to configure this Faraday::Connection. # Any of these values will be set on every request made, unless # overridden for a specific request. # :url - String base URL. # :params - Hash of URI query unencoded key/value pairs. # :headers - Hash of unencoded HTTP header key/value pairs. # :request - Hash of request options. # :ssl - Hash of SSL options. # :proxy - Hash of Proxy options. # # Examples # # Faraday.new 'http://faraday.com' # # # http://faraday.com?page=1 # Faraday.new 'http://faraday.com', :params => {:page => 1} # # # same # # Faraday.new :url => 'http://faraday.com', # :params => {:page => 1} # # Returns a Faraday::Connection. def new(url = nil, options = nil) block = block_given? ? Proc.new : nil options = options ? default_connection_options.merge(options) : default_connection_options.dup Faraday::Connection.new(url, options, &block) end # Internal: Requires internal Faraday libraries. # # *libs - One or more relative String names to Faraday classes. # # Returns nothing. def require_libs(*libs) libs.each do |lib| require "#{lib_path}/#{lib}" end end # Public: Updates default adapter while resetting # #default_connection. # # Returns the new default_adapter. def default_adapter=(adapter) @default_connection = nil @default_adapter = adapter end alias require_lib require_libs private # Internal: Proxies method calls on the Faraday constant to # #default_connection. def method_missing(name, *args, &block) default_connection.send(name, *args, &block) end end self.root_path = File.expand_path "..", __FILE__ self.lib_path = File.expand_path "../faraday", __FILE__ self.default_adapter = :net_http # Gets the default connection used for simple scripts. # # Returns a Faraday::Connection, configured with the #default_adapter. def self.default_connection @default_connection ||= Connection.new end # Gets the default connection options used when calling Faraday#new. # # Returns a Faraday::ConnectionOptions. def self.default_connection_options @default_connection_options ||= ConnectionOptions.new end if (!defined?(RUBY_ENGINE) || "ruby" == RUBY_ENGINE) && RUBY_VERSION < '1.9' begin require 'system_timer' Timer = SystemTimer rescue LoadError warn "Faraday: you may want to install system_timer for reliable timeouts" end end unless const_defined? :Timer require 'timeout' Timer = Timeout end # Public: Adds the ability for other modules to register and lookup # middleware classes. module MiddlewareRegistry # Public: Register middleware class(es) on the current module. # # mapping - A Hash mapping Symbol keys to classes. Classes can be expressed # as fully qualified constant, or a Proc that will be lazily # called to return the former. # # Examples # # module Faraday # class Whatever # # Middleware looked up by :foo returns Faraday::Whatever::Foo. # register_middleware :foo => Foo # # # Middleware looked up by :bar returns Faraday::Whatever.const_get(:Bar) # register_middleware :bar => :Bar # # # Middleware looked up by :baz requires 'baz' and returns Faraday::Whatever.const_get(:Baz) # register_middleware :baz => [:Baz, 'baz'] # end # end # # Returns nothing. def register_middleware(autoload_path = nil, mapping = nil) if mapping.nil? mapping = autoload_path autoload_path = nil end middleware_mutex do @middleware_autoload_path = autoload_path if autoload_path (@registered_middleware ||= {}).update(mapping) end end # Public: Lookup middleware class with a registered Symbol shortcut. # # key - The Symbol key for the registered middleware. # # Examples # # module Faraday # class Whatever # register_middleware :foo => Foo # end # end # # Faraday::Whatever.lookup_middleware(:foo) # # => Faraday::Whatever::Foo # # Returns a middleware Class. def lookup_middleware(key) load_middleware(key) || raise(Faraday::Error.new("#{key.inspect} is not registered on #{self}")) end def middleware_mutex(&block) @middleware_mutex ||= begin require 'monitor' Monitor.new end @middleware_mutex.synchronize(&block) end def fetch_middleware(key) defined?(@registered_middleware) && @registered_middleware[key] end def load_middleware(key) value = fetch_middleware(key) case value when Module value when Symbol, String middleware_mutex do @registered_middleware[key] = const_get(value) end when Proc middleware_mutex do @registered_middleware[key] = value.call end when Array middleware_mutex do const, path = value if root = @middleware_autoload_path path = "#{root}/#{path}" end require(path) @registered_middleware[key] = const end load_middleware(key) end end end def self.const_missing(name) if name.to_sym == :Builder warn "Faraday::Builder is now Faraday::RackBuilder." const_set name, RackBuilder else super end end require_libs "utils", "options", "connection", "rack_builder", "parameters", "middleware", "adapter", "request", "response", "upload_io", "error" if !ENV["FARADAY_NO_AUTOLOAD"] require_lib 'autoload' end end # not pulling in active-support JUST for this method. And I love this method. class Object # The primary purpose of this method is to "tap into" a method chain, # in order to perform operations on intermediate results within the chain. # # Examples # # (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a. # tap { |x| puts "array: #{x.inspect}" }. # select { |x| x%2 == 0 }. # tap { |x| puts "evens: #{x.inspect}" }. # map { |x| x*x }. # tap { |x| puts "squares: #{x.inspect}" } # # Yields self. # Returns self. def tap yield(self) self end unless Object.respond_to?(:tap) end faraday-0.9.2/lib/faraday/000077500000000000000000000000001260477344700153245ustar00rootroot00000000000000faraday-0.9.2/lib/faraday/adapter.rb000066400000000000000000000027061260477344700172760ustar00rootroot00000000000000module Faraday # Public: This is a base class for all Faraday adapters. Adapters are # responsible for fulfilling a Faraday request. class Adapter < Middleware CONTENT_LENGTH = 'Content-Length'.freeze register_middleware File.expand_path('../adapter', __FILE__), :test => [:Test, 'test'], :net_http => [:NetHttp, 'net_http'], :net_http_persistent => [:NetHttpPersistent, 'net_http_persistent'], :typhoeus => [:Typhoeus, 'typhoeus'], :patron => [:Patron, 'patron'], :em_synchrony => [:EMSynchrony, 'em_synchrony'], :em_http => [:EMHttp, 'em_http'], :excon => [:Excon, 'excon'], :rack => [:Rack, 'rack'], :httpclient => [:HTTPClient, 'httpclient'] # Public: This module marks an Adapter as supporting parallel requests. module Parallelism attr_writer :supports_parallel def supports_parallel?() @supports_parallel end def inherited(subclass) super subclass.supports_parallel = self.supports_parallel? end end extend Parallelism self.supports_parallel = false def call(env) env.clear_body if env.needs_body? end def save_response(env, status, body, headers = nil) env.status = status env.body = body env.response_headers = Utils::Headers.new.tap do |response_headers| response_headers.update headers unless headers.nil? yield(response_headers) if block_given? end end end end faraday-0.9.2/lib/faraday/adapter/000077500000000000000000000000001260477344700167445ustar00rootroot00000000000000faraday-0.9.2/lib/faraday/adapter/em_http.rb000066400000000000000000000154561260477344700207440ustar00rootroot00000000000000module Faraday class Adapter # EventMachine adapter is useful for either asynchronous requests # when in EM reactor loop or for making parallel requests in # synchronous code. class EMHttp < Faraday::Adapter module Options def connection_config(env) options = {} configure_proxy(options, env) configure_timeout(options, env) configure_socket(options, env) configure_ssl(options, env) options end def request_config(env) options = { :body => read_body(env), :head => env[:request_headers], # :keepalive => true, # :file => 'path/to/file', # stream data off disk } configure_compression(options, env) options end def read_body(env) body = env[:body] body.respond_to?(:read) ? body.read : body end def configure_proxy(options, env) if proxy = request_options(env)[:proxy] options[:proxy] = { :host => proxy[:uri].host, :port => proxy[:uri].port, :authorization => [proxy[:user], proxy[:password]] } end end def configure_socket(options, env) if bind = request_options(env)[:bind] options[:bind] = { :host => bind[:host], :port => bind[:port] } end end def configure_ssl(options, env) if env[:url].scheme == 'https' && env[:ssl] options[:ssl] = { :cert_chain_file => env[:ssl][:ca_file], :verify_peer => env[:ssl].fetch(:verify, true) } end end def configure_timeout(options, env) timeout, open_timeout = request_options(env).values_at(:timeout, :open_timeout) options[:connect_timeout] = options[:inactivity_timeout] = timeout options[:connect_timeout] = open_timeout if open_timeout end def configure_compression(options, env) if env[:method] == :get and not options[:head].key? 'accept-encoding' options[:head]['accept-encoding'] = 'gzip, compressed' end end def request_options(env) env[:request] end end include Options dependency 'em-http' self.supports_parallel = true def self.setup_parallel_manager(options = nil) Manager.new end def call(env) super perform_request env @app.call env end def perform_request(env) if parallel?(env) manager = env[:parallel_manager] manager.add { perform_single_request(env). callback { env[:response].finish(env) } } else unless EventMachine.reactor_running? error = nil # start EM, block until request is completed EventMachine.run do perform_single_request(env). callback { EventMachine.stop }. errback { |client| error = error_message(client) EventMachine.stop } end raise_error(error) if error else # EM is running: instruct upstream that this is an async request env[:parallel_manager] = true perform_single_request(env). callback { env[:response].finish(env) }. errback { # TODO: no way to communicate the error in async mode raise NotImplementedError } end end rescue EventMachine::Connectify::CONNECTError => err if err.message.include?("Proxy Authentication Required") raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "} else raise Error::ConnectionFailed, err end rescue => err if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err raise Faraday::SSLError, err else raise end end # TODO: reuse the connection to support pipelining def perform_single_request(env) req = EventMachine::HttpRequest.new(env[:url], connection_config(env)) req.setup_request(env[:method], request_config(env)).callback { |client| save_response(env, client.response_header.status, client.response) do |resp_headers| client.response_header.each do |name, value| resp_headers[name.to_sym] = value end end } end def error_message(client) client.error or "request failed" end def raise_error(msg) errklass = Faraday::Error::ClientError if msg == Errno::ETIMEDOUT errklass = Faraday::Error::TimeoutError msg = "request timed out" elsif msg == Errno::ECONNREFUSED errklass = Faraday::Error::ConnectionFailed msg = "connection refused" elsif msg == "connection closed by server" errklass = Faraday::Error::ConnectionFailed end raise errklass, msg end def parallel?(env) !!env[:parallel_manager] end # The parallel manager is designed to start an EventMachine loop # and block until all registered requests have been completed. class Manager def initialize reset end def reset @registered_procs = [] @num_registered = 0 @num_succeeded = 0 @errors = [] @running = false end def running?() @running end def add if running? perform_request { yield } else @registered_procs << Proc.new end @num_registered += 1 end def run if @num_registered > 0 @running = true EventMachine.run do @registered_procs.each do |proc| perform_request(&proc) end end if @errors.size > 0 raise Faraday::Error::ClientError, @errors.first || "connection failed" end end ensure reset end def perform_request client = yield client.callback { @num_succeeded += 1; check_finished } client.errback { @errors << client.error; check_finished } end def check_finished if @num_succeeded + @errors.size == @num_registered EventMachine.stop end end end end end end begin require 'openssl' rescue LoadError warn "Warning: no such file to load -- openssl. Make sure it is installed if you want HTTPS support" else require 'faraday/adapter/em_http_ssl_patch' end if Faraday::Adapter::EMHttp.loaded? faraday-0.9.2/lib/faraday/adapter/em_http_ssl_patch.rb000066400000000000000000000024671260477344700230020ustar00rootroot00000000000000require 'openssl' require 'em-http' module EmHttpSslPatch def ssl_verify_peer(cert_string) cert = nil begin cert = OpenSSL::X509::Certificate.new(cert_string) rescue OpenSSL::X509::CertificateError return false end @last_seen_cert = cert if certificate_store.verify(@last_seen_cert) begin certificate_store.add_cert(@last_seen_cert) rescue OpenSSL::X509::StoreError => e raise e unless e.message == 'cert already in hash table' end true else raise OpenSSL::SSL::SSLError.new(%(unable to verify the server certificate for "#{host}")) end end def ssl_handshake_completed return true unless verify_peer? unless OpenSSL::SSL.verify_certificate_identity(@last_seen_cert, host) raise OpenSSL::SSL::SSLError.new(%(host "#{host}" does not match the server certificate)) else true end end def verify_peer? parent.connopts.tls[:verify_peer] end def host parent.connopts.host end def certificate_store @certificate_store ||= begin store = OpenSSL::X509::Store.new store.set_default_paths ca_file = parent.connopts.tls[:cert_chain_file] store.add_file(ca_file) if ca_file store end end end EventMachine::HttpStubConnection.send(:include, EmHttpSslPatch) faraday-0.9.2/lib/faraday/adapter/em_synchrony.rb000066400000000000000000000055161260477344700220150ustar00rootroot00000000000000require 'uri' module Faraday class Adapter class EMSynchrony < Faraday::Adapter include EMHttp::Options dependency do require 'em-synchrony/em-http' require 'em-synchrony/em-multi' require 'fiber' end self.supports_parallel = true def self.setup_parallel_manager(options = {}) ParallelManager.new end def call(env) super request = EventMachine::HttpRequest.new(Utils::URI(env[:url].to_s), connection_config(env)) http_method = env[:method].to_s.downcase.to_sym # Queue requests for parallel execution. if env[:parallel_manager] env[:parallel_manager].add(request, http_method, request_config(env)) do |resp| save_response(env, resp.response_header.status, resp.response) do |resp_headers| resp.response_header.each do |name, value| resp_headers[name.to_sym] = value end end # Finalize the response object with values from `env`. env[:response].finish(env) end # Execute single request. else client = nil block = lambda { request.send(http_method, request_config(env)) } if !EM.reactor_running? EM.run do Fiber.new { client = block.call EM.stop }.resume end else client = block.call end raise client.error if client.error save_response(env, client.response_header.status, client.response) do |resp_headers| client.response_header.each do |name, value| resp_headers[name.to_sym] = value end end end @app.call env rescue Errno::ECONNREFUSED raise Error::ConnectionFailed, $! rescue EventMachine::Connectify::CONNECTError => err if err.message.include?("Proxy Authentication Required") raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "} else raise Error::ConnectionFailed, err end rescue Errno::ETIMEDOUT => err raise Error::TimeoutError, err rescue RuntimeError => err if err.message == "connection closed by server" raise Error::ConnectionFailed, err else raise end rescue => err if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err raise Faraday::SSLError, err else raise end end end end end require 'faraday/adapter/em_synchrony/parallel_manager' begin require 'openssl' rescue LoadError warn "Warning: no such file to load -- openssl. Make sure it is installed if you want HTTPS support" else require 'faraday/adapter/em_http_ssl_patch' end if Faraday::Adapter::EMSynchrony.loaded? faraday-0.9.2/lib/faraday/adapter/em_synchrony/000077500000000000000000000000001260477344700214615ustar00rootroot00000000000000faraday-0.9.2/lib/faraday/adapter/em_synchrony/parallel_manager.rb000066400000000000000000000031271260477344700252770ustar00rootroot00000000000000module Faraday class Adapter class EMSynchrony < Faraday::Adapter class ParallelManager # Add requests to queue. The `request` argument should be a # `EM::HttpRequest` object. def add(request, method, *args, &block) queue << { :request => request, :method => method, :args => args, :block => block } end # Run all requests on queue with `EM::Synchrony::Multi`, wrapping # it in a reactor and fiber if needed. def run result = nil if !EM.reactor_running? EM.run { Fiber.new do result = perform EM.stop end.resume } else result = perform end result end private # The request queue. def queue @queue ||= [] end # Main `EM::Synchrony::Multi` performer. def perform multi = ::EM::Synchrony::Multi.new queue.each do |item| method = "a#{item[:method]}".to_sym req = item[:request].send(method, *item[:args]) req.callback(&item[:block]) req_name = "req_#{multi.requests.size}".to_sym multi.add(req_name, req) end # Clear the queue, so parallel manager objects can be reused. @queue = [] # Block fiber until all requests have returned. multi.perform end end # ParallelManager end # EMSynchrony end # Adapter end # Faraday faraday-0.9.2/lib/faraday/adapter/excon.rb000066400000000000000000000051131260477344700204050ustar00rootroot00000000000000module Faraday class Adapter class Excon < Faraday::Adapter dependency 'excon' def initialize(app, connection_options = {}) @connection_options = connection_options super(app) end def call(env) super opts = {} if env[:url].scheme == 'https' && ssl = env[:ssl] opts[:ssl_verify_peer] = !!ssl.fetch(:verify, true) opts[:ssl_ca_path] = ssl[:ca_path] if ssl[:ca_path] opts[:ssl_ca_file] = ssl[:ca_file] if ssl[:ca_file] opts[:client_cert] = ssl[:client_cert] if ssl[:client_cert] opts[:client_key] = ssl[:client_key] if ssl[:client_key] opts[:certificate] = ssl[:certificate] if ssl[:certificate] opts[:private_key] = ssl[:private_key] if ssl[:private_key] # https://github.com/geemus/excon/issues/106 # https://github.com/jruby/jruby-ossl/issues/19 opts[:nonblock] = false end if ( req = env[:request] ) if req[:timeout] opts[:read_timeout] = req[:timeout] opts[:connect_timeout] = req[:timeout] opts[:write_timeout] = req[:timeout] end if req[:open_timeout] opts[:connect_timeout] = req[:open_timeout] opts[:write_timeout] = req[:open_timeout] end if req[:proxy] opts[:proxy] = { :host => req[:proxy][:uri].host, :hostname => req[:proxy][:uri].hostname, :port => req[:proxy][:uri].port, :scheme => req[:proxy][:uri].scheme, :user => req[:proxy][:user], :password => req[:proxy][:password] } end end conn = ::Excon.new(env[:url].to_s, opts.merge(@connection_options)) resp = conn.request \ :method => env[:method].to_s.upcase, :headers => env[:request_headers], :body => read_body(env) save_response(env, resp.status.to_i, resp.body, resp.headers) @app.call env rescue ::Excon::Errors::SocketError => err if err.message =~ /\btimeout\b/ raise Error::TimeoutError, err elsif err.message =~ /\bcertificate\b/ raise Faraday::SSLError, err else raise Error::ConnectionFailed, err end rescue ::Excon::Errors::Timeout => err raise Error::TimeoutError, err end # TODO: support streaming requests def read_body(env) env[:body].respond_to?(:read) ? env[:body].read : env[:body] end end end end faraday-0.9.2/lib/faraday/adapter/httpclient.rb000066400000000000000000000066071260477344700214600ustar00rootroot00000000000000module Faraday class Adapter class HTTPClient < Faraday::Adapter dependency 'httpclient' def client @client ||= ::HTTPClient.new end def call(env) super # enable compression client.transparent_gzip_decompression = true if req = env[:request] if proxy = req[:proxy] configure_proxy proxy end if bind = req[:bind] configure_socket bind end configure_timeouts req end if env[:url].scheme == 'https' && ssl = env[:ssl] configure_ssl ssl end # TODO Don't stream yet. # https://github.com/nahi/httpclient/pull/90 env[:body] = env[:body].read if env[:body].respond_to? :read resp = client.request env[:method], env[:url], :body => env[:body], :header => env[:request_headers] save_response env, resp.status, resp.body, resp.headers @app.call env rescue ::HTTPClient::TimeoutError, Errno::ETIMEDOUT raise Faraday::Error::TimeoutError, $! rescue ::HTTPClient::BadResponseError => err if err.message.include?('status 407') raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "} else raise Faraday::Error::ClientError, $! end rescue Errno::ECONNREFUSED, EOFError raise Faraday::Error::ConnectionFailed, $! rescue => err if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err raise Faraday::SSLError, err else raise end end def configure_socket(bind) client.socket_local.host = bind[:host] client.socket_local.port = bind[:port] end def configure_proxy(proxy) client.proxy = proxy[:uri] if proxy[:user] && proxy[:password] client.set_proxy_auth proxy[:user], proxy[:password] end end def configure_ssl(ssl) ssl_config = client.ssl_config ssl_config.verify_mode = ssl_verify_mode(ssl) ssl_config.cert_store = ssl_cert_store(ssl) ssl_config.add_trust_ca ssl[:ca_file] if ssl[:ca_file] ssl_config.add_trust_ca ssl[:ca_path] if ssl[:ca_path] ssl_config.client_cert = ssl[:client_cert] if ssl[:client_cert] ssl_config.client_key = ssl[:client_key] if ssl[:client_key] ssl_config.verify_depth = ssl[:verify_depth] if ssl[:verify_depth] end def configure_timeouts(req) if req[:timeout] client.connect_timeout = req[:timeout] client.receive_timeout = req[:timeout] client.send_timeout = req[:timeout] end if req[:open_timeout] client.connect_timeout = req[:open_timeout] client.send_timeout = req[:open_timeout] end end def ssl_cert_store(ssl) return ssl[:cert_store] if ssl[:cert_store] # Use the default cert store by default, i.e. system ca certs cert_store = OpenSSL::X509::Store.new cert_store.set_default_paths cert_store end def ssl_verify_mode(ssl) ssl[:verify_mode] || begin if ssl.fetch(:verify, true) OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT else OpenSSL::SSL::VERIFY_NONE end end end end end end faraday-0.9.2/lib/faraday/adapter/net_http.rb000066400000000000000000000101351260477344700211160ustar00rootroot00000000000000begin require 'net/https' rescue LoadError warn "Warning: no such file to load -- net/https. Make sure openssl is installed if you want ssl support" require 'net/http' end require 'zlib' module Faraday class Adapter class NetHttp < Faraday::Adapter NET_HTTP_EXCEPTIONS = [ EOFError, Errno::ECONNABORTED, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH, Errno::EINVAL, Errno::ENETUNREACH, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, SocketError, Zlib::GzipFile::Error, ] NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL) NET_HTTP_EXCEPTIONS << Net::OpenTimeout if defined?(Net::OpenTimeout) def call(env) super with_net_http_connection(env) do |http| configure_ssl(http, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl] req = env[:request] http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout] http.open_timeout = req[:open_timeout] if req[:open_timeout] begin http_response = perform_request(http, env) rescue *NET_HTTP_EXCEPTIONS => err if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err raise Faraday::SSLError, err else raise Error::ConnectionFailed, err end end save_response(env, http_response.code.to_i, http_response.body || '') do |response_headers| http_response.each_header do |key, value| response_headers[key] = value end end end @app.call env rescue Timeout::Error, Errno::ETIMEDOUT => err raise Faraday::Error::TimeoutError, err end def create_request(env) request = Net::HTTPGenericRequest.new \ env[:method].to_s.upcase, # request method !!env[:body], # is there request body :head != env[:method], # is there response body env[:url].request_uri, # request uri path env[:request_headers] # request headers if env[:body].respond_to?(:read) request.body_stream = env[:body] else request.body = env[:body] end request end def perform_request(http, env) if :get == env[:method] and !env[:body] # prefer `get` to `request` because the former handles gzip (ruby 1.9) http.get env[:url].request_uri, env[:request_headers] else http.request create_request(env) end end def with_net_http_connection(env) yield net_http_connection(env) end def net_http_connection(env) if proxy = env[:request][:proxy] Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password]) else Net::HTTP end.new(env[:url].host, env[:url].port || (env[:url].scheme == 'https' ? 443 : 80)) end def configure_ssl(http, ssl) http.use_ssl = true http.verify_mode = ssl_verify_mode(ssl) http.cert_store = ssl_cert_store(ssl) http.cert = ssl[:client_cert] if ssl[:client_cert] http.key = ssl[:client_key] if ssl[:client_key] http.ca_file = ssl[:ca_file] if ssl[:ca_file] http.ca_path = ssl[:ca_path] if ssl[:ca_path] http.verify_depth = ssl[:verify_depth] if ssl[:verify_depth] http.ssl_version = ssl[:version] if ssl[:version] end def ssl_cert_store(ssl) return ssl[:cert_store] if ssl[:cert_store] # Use the default cert store by default, i.e. system ca certs cert_store = OpenSSL::X509::Store.new cert_store.set_default_paths cert_store end def ssl_verify_mode(ssl) ssl[:verify_mode] || begin if ssl.fetch(:verify, true) OpenSSL::SSL::VERIFY_PEER else OpenSSL::SSL::VERIFY_NONE end end end end end end faraday-0.9.2/lib/faraday/adapter/net_http_persistent.rb000066400000000000000000000034221260477344700233770ustar00rootroot00000000000000# Rely on autoloading instead of explicit require; helps avoid the "already # initialized constant" warning on Ruby 1.8.7 when NetHttp is refereced below. # require 'faraday/adapter/net_http' module Faraday class Adapter class NetHttpPersistent < NetHttp dependency 'net/http/persistent' def with_net_http_connection(env) if proxy = env[:request][:proxy] proxy_uri = ::URI::HTTP === proxy[:uri] ? proxy[:uri].dup : ::URI.parse(proxy[:uri].to_s) proxy_uri.user = proxy_uri.password = nil # awful patch for net-http-persistent 2.8 not unescaping user/password (class << proxy_uri; self; end).class_eval do define_method(:user) { proxy[:user] } define_method(:password) { proxy[:password] } end if proxy[:user] end yield Net::HTTP::Persistent.new 'Faraday', proxy_uri end def perform_request(http, env) http.request env[:url], create_request(env) rescue Errno::ETIMEDOUT => error raise Faraday::Error::TimeoutError, error rescue Net::HTTP::Persistent::Error => error if error.message.include? 'Timeout' raise Faraday::Error::TimeoutError, error elsif error.message.include? 'connection refused' raise Faraday::Error::ConnectionFailed, error else raise end end def configure_ssl(http, ssl) http.verify_mode = ssl_verify_mode(ssl) http.cert_store = ssl_cert_store(ssl) http.certificate = ssl[:client_cert] if ssl[:client_cert] http.private_key = ssl[:client_key] if ssl[:client_key] http.ca_file = ssl[:ca_file] if ssl[:ca_file] http.ssl_version = ssl[:version] if ssl[:version] end end end end faraday-0.9.2/lib/faraday/adapter/patron.rb000066400000000000000000000047711260477344700206050ustar00rootroot00000000000000module Faraday class Adapter class Patron < Faraday::Adapter dependency 'patron' def initialize(app, &block) super(app) @block = block end def call(env) super # TODO: support streaming requests env[:body] = env[:body].read if env[:body].respond_to? :read session = @session ||= create_session if req = env[:request] session.timeout = session.connect_timeout = req[:timeout] if req[:timeout] session.connect_timeout = req[:open_timeout] if req[:open_timeout] if proxy = req[:proxy] proxy_uri = proxy[:uri].dup proxy_uri.user = proxy[:user] && Utils.escape(proxy[:user]).gsub('+', '%20') proxy_uri.password = proxy[:password] && Utils.escape(proxy[:password]).gsub('+', '%20') session.proxy = proxy_uri.to_s end end response = begin data = env[:body] ? env[:body].to_s : nil session.request(env[:method], env[:url].to_s, env[:request_headers], :data => data) rescue Errno::ECONNREFUSED, ::Patron::ConnectionFailed raise Error::ConnectionFailed, $! end save_response(env, response.status, response.body, response.headers) @app.call env rescue ::Patron::TimeoutError => err if err.message == "Connection time-out" raise Faraday::Error::ConnectionFailed, err else raise Faraday::Error::TimeoutError, err end rescue ::Patron::Error => err if err.message.include?("code 407") raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "} else raise Error::ConnectionFailed, err end end if loaded? && defined?(::Patron::Request::VALID_ACTIONS) # HAX: helps but doesn't work completely # https://github.com/toland/patron/issues/34 ::Patron::Request::VALID_ACTIONS.tap do |actions| if actions[0].is_a?(Symbol) actions << :patch unless actions.include? :patch actions << :options unless actions.include? :options else # Patron 0.4.20 and up actions << "PATCH" unless actions.include? "PATCH" actions << "OPTIONS" unless actions.include? "OPTIONS" end end end def create_session session = ::Patron::Session.new session.insecure = true @block.call(session) if @block session end end end end faraday-0.9.2/lib/faraday/adapter/rack.rb000066400000000000000000000031651260477344700202160ustar00rootroot00000000000000module Faraday class Adapter # Sends requests to a Rack app. # # Examples # # class MyRackApp # def call(env) # [200, {'Content-Type' => 'text/html'}, ["hello world"]] # end # end # # Faraday.new do |conn| # conn.adapter :rack, MyRackApp.new # end class Rack < Faraday::Adapter dependency 'rack/test' # not prefixed with "HTTP_" SPECIAL_HEADERS = %w[ CONTENT_LENGTH CONTENT_TYPE ] def initialize(faraday_app, rack_app) super(faraday_app) mock_session = ::Rack::MockSession.new(rack_app) @session = ::Rack::Test::Session.new(mock_session) end def call(env) super rack_env = { :method => env[:method], :input => env[:body].respond_to?(:read) ? env[:body].read : env[:body], 'rack.url_scheme' => env[:url].scheme } env[:request_headers].each do |name, value| name = name.upcase.tr('-', '_') name = "HTTP_#{name}" unless SPECIAL_HEADERS.include? name rack_env[name] = value end if env[:request_headers] timeout = env[:request][:timeout] || env[:request][:open_timeout] response = if timeout Timer.timeout(timeout, Faraday::Error::TimeoutError) { execute_request(env, rack_env) } else execute_request(env, rack_env) end save_response(env, response.status, response.body, response.headers) @app.call env end def execute_request(env, rack_env) @session.request(env[:url].to_s, rack_env) end end end end faraday-0.9.2/lib/faraday/adapter/test.rb000066400000000000000000000112201260477344700202440ustar00rootroot00000000000000module Faraday class Adapter # test = Faraday::Connection.new do # use Faraday::Adapter::Test do |stub| # stub.get '/nigiri/sake.json' do # [200, {}, 'hi world'] # end # end # end # # resp = test.get '/nigiri/sake.json' # resp.body # => 'hi world' # class Test < Faraday::Adapter attr_accessor :stubs class Stubs class NotFound < StandardError end def initialize # {:get => [Stub, Stub]} @stack, @consumed = {}, {} yield(self) if block_given? end def empty? @stack.empty? end def match(request_method, path, headers, body) return false if !@stack.key?(request_method) stack = @stack[request_method] consumed = (@consumed[request_method] ||= []) if stub = matches?(stack, path, headers, body) consumed << stack.delete(stub) stub else matches?(consumed, path, headers, body) end end def get(path, headers = {}, &block) new_stub(:get, path, headers, &block) end def head(path, headers = {}, &block) new_stub(:head, path, headers, &block) end def post(path, body=nil, headers = {}, &block) new_stub(:post, path, headers, body, &block) end def put(path, body=nil, headers = {}, &block) new_stub(:put, path, headers, body, &block) end def patch(path, body=nil, headers = {}, &block) new_stub(:patch, path, headers, body, &block) end def delete(path, headers = {}, &block) new_stub(:delete, path, headers, &block) end def options(path, headers = {}, &block) new_stub(:options, path, headers, &block) end # Raises an error if any of the stubbed calls have not been made. def verify_stubbed_calls failed_stubs = [] @stack.each do |method, stubs| unless stubs.size == 0 failed_stubs.concat(stubs.map {|stub| "Expected #{method} #{stub}." }) end end raise failed_stubs.join(" ") unless failed_stubs.size == 0 end protected def new_stub(request_method, path, headers = {}, body=nil, &block) normalized_path = Faraday::Utils.normalize_path(path) (@stack[request_method] ||= []) << Stub.new(normalized_path, headers, body, block) end def matches?(stack, path, headers, body) stack.detect { |stub| stub.matches?(path, headers, body) } end end class Stub < Struct.new(:path, :params, :headers, :body, :block) def initialize(full, headers, body, block) path, query = full.split('?') params = query ? Faraday::Utils.parse_nested_query(query) : {} super(path, params, headers, body, block) end def matches?(request_uri, request_headers, request_body) request_path, request_query = request_uri.split('?') request_params = request_query ? Faraday::Utils.parse_nested_query(request_query) : {} request_path == path && params_match?(request_params) && (body.to_s.size.zero? || request_body == body) && headers_match?(request_headers) end def params_match?(request_params) params.keys.all? do |key| request_params[key] == params[key] end end def headers_match?(request_headers) headers.keys.all? do |key| request_headers[key] == headers[key] end end def to_s "#{path} #{body}" end end def initialize(app, stubs=nil, &block) super(app) @stubs = stubs || Stubs.new configure(&block) if block end def configure yield(stubs) end def call(env) super normalized_path = Faraday::Utils.normalize_path(env[:url]) params_encoder = env.request.params_encoder || Faraday::Utils.default_params_encoder if stub = stubs.match(env[:method], normalized_path, env.request_headers, env[:body]) env[:params] = (query = env[:url].query) ? params_encoder.decode(query) : {} status, headers, body = stub.block.call(env) save_response(env, status, body, headers) else raise Stubs::NotFound, "no stubbed request for #{env[:method]} #{normalized_path} #{env[:body]}" end @app.call(env) end end end end faraday-0.9.2/lib/faraday/adapter/typhoeus.rb000066400000000000000000000070711260477344700211560ustar00rootroot00000000000000module Faraday class Adapter class Typhoeus < Faraday::Adapter self.supports_parallel = true def self.setup_parallel_manager(options = {}) options.empty? ? ::Typhoeus::Hydra.hydra : ::Typhoeus::Hydra.new(options) end dependency 'typhoeus' def call(env) super perform_request env @app.call env end def perform_request(env) read_body env hydra = env[:parallel_manager] || self.class.setup_parallel_manager hydra.queue request(env) hydra.run unless parallel?(env) rescue Errno::ECONNREFUSED raise Error::ConnectionFailed, $! end # TODO: support streaming requests def read_body(env) env[:body] = env[:body].read if env[:body].respond_to? :read end def request(env) method = env[:method] # For some reason, prevents Typhoeus from using "100-continue". # We want this because Webrick 1.3.1 can't seem to handle it w/ PUT. method = method.to_s.upcase if method == :put req = ::Typhoeus::Request.new env[:url].to_s, :method => method, :body => env[:body], :headers => env[:request_headers], :disable_ssl_peer_verification => (env[:ssl] && env[:ssl].disable?) configure_ssl req, env configure_proxy req, env configure_timeout req, env configure_socket req, env req.on_complete do |resp| if resp.timed_out? if parallel?(env) # TODO: error callback in async mode else raise Faraday::Error::TimeoutError, "request timed out" end end case resp.curl_return_code when 0 # everything OK when 7 raise Error::ConnectionFailed, resp.curl_error_message when 60 raise Faraday::SSLError, resp.curl_error_message else raise Error::ClientError, resp.curl_error_message end save_response(env, resp.code, resp.body) do |response_headers| response_headers.parse resp.headers end # in async mode, :response is initialized at this point env[:response].finish(env) if parallel?(env) end req end def configure_ssl(req, env) ssl = env[:ssl] req.ssl_version = ssl[:version] if ssl[:version] req.ssl_cert = ssl[:client_cert] if ssl[:client_cert] req.ssl_key = ssl[:client_key] if ssl[:client_key] req.ssl_cacert = ssl[:ca_file] if ssl[:ca_file] req.ssl_capath = ssl[:ca_path] if ssl[:ca_path] end def configure_proxy(req, env) proxy = request_options(env)[:proxy] return unless proxy req.proxy = "#{proxy[:uri].host}:#{proxy[:uri].port}" if proxy[:user] && proxy[:password] req.proxy_username = proxy[:user] req.proxy_password = proxy[:password] end end def configure_timeout(req, env) env_req = request_options(env) req.timeout = req.connect_timeout = (env_req[:timeout] * 1000) if env_req[:timeout] req.connect_timeout = (env_req[:open_timeout] * 1000) if env_req[:open_timeout] end def configure_socket(req, env) if bind = request_options(env)[:bind] req.interface = bind[:host] end end def request_options(env) env[:request] end def parallel?(env) !!env[:parallel_manager] end end end end faraday-0.9.2/lib/faraday/autoload.rb000066400000000000000000000046551260477344700174730ustar00rootroot00000000000000module Faraday # Internal: Adds the ability for other modules to manage autoloadable # constants. module AutoloadHelper # Internal: Registers the constants to be auto loaded. # # prefix - The String require prefix. If the path is inside Faraday, then # it will be prefixed with the root path of this loaded Faraday # version. # options - Hash of Symbol => String library names. # # Examples. # # Faraday.autoload_all 'faraday/foo', # :Bar => 'bar' # # # requires faraday/foo/bar to load Faraday::Bar. # Faraday::Bar # # # Returns nothing. def autoload_all(prefix, options) if prefix =~ /^faraday(\/|$)/i prefix = File.join(Faraday.root_path, prefix) end options.each do |const_name, path| autoload const_name, File.join(prefix, path) end end # Internal: Loads each autoloaded constant. If thread safety is a concern, # wrap this in a Mutex. # # Returns nothing. def load_autoloaded_constants constants.each do |const| const_get(const) if autoload?(const) end end # Internal: Filters the module's contents with those that have been already # autoloaded. # # Returns an Array of Class/Module objects. def all_loaded_constants constants.map { |c| const_get(c) }. select { |a| a.respond_to?(:loaded?) && a.loaded? } end end class Adapter extend AutoloadHelper autoload_all 'faraday/adapter', :NetHttp => 'net_http', :NetHttpPersistent => 'net_http_persistent', :Typhoeus => 'typhoeus', :EMSynchrony => 'em_synchrony', :EMHttp => 'em_http', :Patron => 'patron', :Excon => 'excon', :Test => 'test', :Rack => 'rack', :HTTPClient => 'httpclient' end class Request extend AutoloadHelper autoload_all 'faraday/request', :UrlEncoded => 'url_encoded', :Multipart => 'multipart', :Retry => 'retry', :Authorization => 'authorization', :BasicAuthentication => 'basic_authentication', :TokenAuthentication => 'token_authentication', :Instrumentation => 'instrumentation' end class Response extend AutoloadHelper autoload_all 'faraday/response', :RaiseError => 'raise_error', :Logger => 'logger' end end faraday-0.9.2/lib/faraday/connection.rb000066400000000000000000000334021260477344700200120ustar00rootroot00000000000000module Faraday # Public: Connection objects manage the default properties and the middleware # stack for fulfilling an HTTP request. # # Examples # # conn = Faraday::Connection.new 'http://sushi.com' # # # GET http://sushi.com/nigiri # conn.get 'nigiri' # # => # # class Connection # A Set of allowed HTTP verbs. METHODS = Set.new [:get, :post, :put, :delete, :head, :patch, :options] # Public: Returns a Hash of URI query unencoded key/value pairs. attr_reader :params # Public: Returns a Hash of unencoded HTTP header key/value pairs. attr_reader :headers # Public: Returns a URI with the prefix used for all requests from this # Connection. This includes a default host name, scheme, port, and path. attr_reader :url_prefix # Public: Returns the Faraday::Builder for this Connection. attr_reader :builder # Public: Returns a Hash of the request options. attr_reader :options # Public: Returns a Hash of the SSL options. attr_reader :ssl # Public: Returns the parallel manager for this Connection. attr_reader :parallel_manager # Public: Sets the default parallel manager for this connection. attr_writer :default_parallel_manager # Public: Initializes a new Faraday::Connection. # # url - URI or String base URL to use as a prefix for all # requests (optional). # options - Hash or Faraday::ConnectionOptions. # :url - URI or String base URL (default: "http:/"). # :params - Hash of URI query unencoded key/value pairs. # :headers - Hash of unencoded HTTP header key/value pairs. # :request - Hash of request options. # :ssl - Hash of SSL options. # :proxy - URI, String or Hash of HTTP proxy options # (default: "http_proxy" environment variable). # :uri - URI or String # :user - String (optional) # :password - String (optional) def initialize(url = nil, options = nil) if url.is_a?(Hash) options = ConnectionOptions.from(url) url = options.url else options = ConnectionOptions.from(options) end @parallel_manager = nil @headers = Utils::Headers.new @params = Utils::ParamsHash.new @options = options.request @ssl = options.ssl @default_parallel_manager = options.parallel_manager @builder = options.builder || begin # pass an empty block to Builder so it doesn't assume default middleware options.new_builder(block_given? ? Proc.new { |b| } : nil) end self.url_prefix = url || 'http:/' @params.update(options.params) if options.params @headers.update(options.headers) if options.headers @proxy = nil proxy(options.fetch(:proxy) { uri = ENV['http_proxy'] if uri && !uri.empty? uri = 'http://' + uri if uri !~ /^http/i uri end }) yield(self) if block_given? @headers[:user_agent] ||= "Faraday v#{VERSION}" end # Public: Sets the Hash of URI query unencoded key/value pairs. def params=(hash) @params.replace hash end # Public: Sets the Hash of unencoded HTTP header key/value pairs. def headers=(hash) @headers.replace hash end extend Forwardable def_delegators :builder, :build, :use, :request, :response, :adapter, :app # Public: Makes an HTTP request without a body. # # url - The optional String base URL to use as a prefix for all # requests. Can also be the options Hash. # params - Hash of URI query unencoded key/value pairs. # headers - Hash of unencoded HTTP header key/value pairs. # # Examples # # conn.get '/items', {:page => 1}, :accept => 'application/json' # conn.head '/items/1' # # # ElasticSearch example sending a body with GET. # conn.get '/twitter/tweet/_search' do |req| # req.headers[:content_type] = 'application/json' # req.params[:routing] = 'kimchy' # req.body = JSON.generate(:query => {...}) # end # # Yields a Faraday::Response for further request customizations. # Returns a Faraday::Response. # # Signature # # (url = nil, params = nil, headers = nil) # # verb - An HTTP verb: get, head, or delete. %w[get head delete].each do |method| class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{method}(url = nil, params = nil, headers = nil) run_request(:#{method}, url, nil, headers) { |request| request.params.update(params) if params yield(request) if block_given? } end RUBY end # Public: Makes an HTTP request with a body. # # url - The optional String base URL to use as a prefix for all # requests. Can also be the options Hash. # body - The String body for the request. # headers - Hash of unencoded HTTP header key/value pairs. # # Examples # # conn.post '/items', data, :content_type => 'application/json' # # # Simple ElasticSearch indexing sample. # conn.post '/twitter/tweet' do |req| # req.headers[:content_type] = 'application/json' # req.params[:routing] = 'kimchy' # req.body = JSON.generate(:user => 'kimchy', ...) # end # # Yields a Faraday::Response for further request customizations. # Returns a Faraday::Response. # # Signature # # (url = nil, body = nil, headers = nil) # # verb - An HTTP verb: post, put, or patch. %w[post put patch].each do |method| class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{method}(url = nil, body = nil, headers = nil, &block) run_request(:#{method}, url, body, headers, &block) end RUBY end # Public: Sets up the Authorization header with these credentials, encoded # with base64. # # login - The authentication login. # pass - The authentication password. # # Examples # # conn.basic_auth 'Aladdin', 'open sesame' # conn.headers['Authorization'] # # => "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" # # Returns nothing. def basic_auth(login, pass) set_authorization_header(:basic_auth, login, pass) end # Public: Sets up the Authorization header with the given token. # # token - The String token. # options - Optional Hash of extra token options. # # Examples # # conn.token_auth 'abcdef', :foo => 'bar' # conn.headers['Authorization'] # # => "Token token=\"abcdef\", # foo=\"bar\"" # # Returns nothing. def token_auth(token, options = nil) set_authorization_header(:token_auth, token, options) end # Public: Sets up a custom Authorization header. # # type - The String authorization type. # token - The String or Hash token. A String value is taken literally, and # a Hash is encoded into comma separated key/value pairs. # # Examples # # conn.authorization :Bearer, 'mF_9.B5f-4.1JqM' # conn.headers['Authorization'] # # => "Bearer mF_9.B5f-4.1JqM" # # conn.authorization :Token, :token => 'abcdef', :foo => 'bar' # conn.headers['Authorization'] # # => "Token token=\"abcdef\", # foo=\"bar\"" # # Returns nothing. def authorization(type, token) set_authorization_header(:authorization, type, token) end # Internal: Traverse the middleware stack in search of a # parallel-capable adapter. # # Yields in case of not found. # # Returns a parallel manager or nil if not found. def default_parallel_manager @default_parallel_manager ||= begin handler = @builder.handlers.detect do |h| h.klass.respond_to?(:supports_parallel?) and h.klass.supports_parallel? end if handler handler.klass.setup_parallel_manager elsif block_given? yield end end end # Public: Determine if this Faraday::Connection can make parallel requests. # # Returns true or false. def in_parallel? !!@parallel_manager end # Public: Sets up the parallel manager to make a set of requests. # # manager - The parallel manager that this Connection's Adapter uses. # # Yields a block to execute multiple requests. # Returns nothing. def in_parallel(manager = nil) @parallel_manager = manager || default_parallel_manager { warn "Warning: `in_parallel` called but no parallel-capable adapter on Faraday stack" warn caller[2,10].join("\n") nil } yield @parallel_manager && @parallel_manager.run ensure @parallel_manager = nil end # Public: Gets or Sets the Hash proxy options. def proxy(arg = nil) return @proxy if arg.nil? @proxy = ProxyOptions.from(arg) end def_delegators :url_prefix, :scheme, :scheme=, :host, :host=, :port, :port= def_delegator :url_prefix, :path, :path_prefix # Public: Parses the giving url with URI and stores the individual # components in this connection. These components serve as defaults for # requests made by this connection. # # url - A String or URI. # # Examples # # 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 # # Returns the parsed URI from teh given input.. def url_prefix=(url, encoder = nil) uri = @url_prefix = Utils.URI(url) self.path_prefix = uri.path params.merge_query(uri.query, encoder) uri.query = nil with_uri_credentials(uri) do |user, password| basic_auth user, password uri.user = uri.password = nil end uri end # Public: Sets the path prefix and ensures that it always has a leading # slash. # # value - A String. # # Returns the new String path prefix. def path_prefix=(value) url_prefix.path = if value value = '/' + value unless value[0,1] == '/' value end end # Public: 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 = nil, extra_params = nil) uri = build_exclusive_url(url) query_values = params.dup.merge_query(uri.query, options.params_encoder) query_values.update extra_params if extra_params uri.query = query_values.empty? ? nil : query_values.to_query(options.params_encoder) uri end # Builds and runs the Faraday::Request. # # method - The Symbol HTTP method. # url - The String or URI to access. # body - The String body # headers - Hash of unencoded HTTP header key/value pairs. # # Returns a Faraday::Response. def run_request(method, url, body, headers) if !METHODS.include?(method) raise ArgumentError, "unknown http method: #{method}" end request = build_request(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 builder.build_response(self, request) end # Creates and configures the request object. # # Returns the new Request. def build_request(method) Request.create(method) do |req| req.params = self.params.dup req.headers = self.headers.dup req.options = self.options.merge(:proxy => self.proxy) yield(req) if block_given? end end # Internal: Build an absolute URL based on url_prefix. # # url - A String or URI-like object # params - A Faraday::Utils::ParamsHash to replace the query values # of the resulting url (default: nil). # # Returns the resulting URI instance. def build_exclusive_url(url = nil, params = nil, params_encoder = nil) url = nil if url.respond_to?(:empty?) and url.empty? base = url_prefix if url and base.path and base.path !~ /\/$/ base = base.dup base.path = base.path + '/' # ensure trailing slash end uri = url ? base + url : base uri.query = params.to_query(params_encoder || options.params_encoder) if params uri.query = nil if uri.query and uri.query.empty? uri end # Internal: Creates a duplicate of this Faraday::Connection. # # Returns a Faraday::Connection. def dup self.class.new(build_exclusive_url, :headers => headers.dup, :params => params.dup, :builder => builder.dup, :ssl => ssl.dup, :request => options.dup) end # Internal: Yields username and password extracted from a URI if they both exist. def with_uri_credentials(uri) if uri.user and uri.password yield(Utils.unescape(uri.user), Utils.unescape(uri.password)) end end def set_authorization_header(header_type, *args) header = Faraday::Request.lookup_middleware(header_type). header(*args) headers[Faraday::Request::Authorization::KEY] = header end end end faraday-0.9.2/lib/faraday/error.rb000066400000000000000000000022371260477344700170060ustar00rootroot00000000000000module Faraday class Error < StandardError; end class MissingDependency < Error; end class ClientError < Error attr_reader :response def initialize(ex, response = nil) @wrapped_exception = nil @response = response if ex.respond_to?(:backtrace) super(ex.message) @wrapped_exception = ex elsif ex.respond_to?(:each_key) super("the server responded with status #{ex[:status]}") @response = ex else super(ex.to_s) end end def backtrace if @wrapped_exception @wrapped_exception.backtrace else super end end def inspect %(#<#{self.class}>) end end class ConnectionFailed < ClientError; end class ResourceNotFound < ClientError; end class ParsingError < ClientError; end class TimeoutError < ClientError def initialize(ex = nil) super(ex || "timeout") end end class SSLError < ClientError end [:MissingDependency, :ClientError, :ConnectionFailed, :ResourceNotFound, :ParsingError, :TimeoutError, :SSLError].each do |const| Error.const_set(const, Faraday.const_get(const)) end end faraday-0.9.2/lib/faraday/middleware.rb000066400000000000000000000013651260477344700177730ustar00rootroot00000000000000module Faraday class Middleware extend MiddlewareRegistry class << self attr_accessor :load_error private :load_error= end self.load_error = nil # Executes a block which should try to require and reference dependent libraries def self.dependency(lib = nil) lib ? require(lib) : yield rescue LoadError, NameError => error self.load_error = error end def self.new(*) raise "missing dependency for #{self}: #{load_error.message}" unless loaded? super end def self.loaded? load_error.nil? end def self.inherited(subclass) super subclass.send(:load_error=, self.load_error) end def initialize(app = nil) @app = app end end end faraday-0.9.2/lib/faraday/options.rb000066400000000000000000000171531260477344700173530ustar00rootroot00000000000000module Faraday # Subclasses Struct with some special helpers for converting from a Hash to # a Struct. class Options < Struct # Public def self.from(value) value ? new.update(value) : new end # Public def each return to_enum(:each) unless block_given? members.each do |key| yield(key.to_sym, send(key)) end end # Public def update(obj) obj.each do |key, value| if sub_options = self.class.options_for(key) value = sub_options.from(value) if value elsif Hash === value hash = {} value.each do |hash_key, hash_value| hash[hash_key] = hash_value end value = hash end self.send("#{key}=", value) unless value.nil? end self end alias merge! update # Public def delete(key) value = send(key) send("#{key}=", nil) value end # Public def clear members.each { |member| delete(member) } end # Public def merge(value) dup.update(value) end # Public def fetch(key, *args) unless symbolized_key_set.include?(key.to_sym) key_setter = "#{key}=" if args.size > 0 send(key_setter, args.first) elsif block_given? send(key_setter, Proc.new.call(key)) else raise self.class.fetch_error_class, "key not found: #{key.inspect}" end end send(key) end # Public def values_at(*keys) keys.map { |key| send(key) } end # Public def keys members.reject { |member| send(member).nil? } end # Public def empty? keys.empty? end # Public def each_key return to_enum(:each_key) unless block_given? keys.each do |key| yield(key) end end # Public def key?(key) keys.include?(key) end alias has_key? key? # Public def each_value return to_enum(:each_value) unless block_given? values.each do |value| yield(value) end end # Public def value?(value) values.include?(value) end alias has_value? value? # Public def to_hash hash = {} members.each do |key| value = send(key) hash[key.to_sym] = value unless value.nil? end hash end # Internal def inspect values = [] members.each do |member| value = send(member) values << "#{member}=#{value.inspect}" if value end values = values.empty? ? ' (empty)' : (' ' << values.join(", ")) %(#<#{self.class}#{values}>) end # Internal def self.options(mapping) attribute_options.update(mapping) end # Internal def self.options_for(key) attribute_options[key] end # Internal def self.attribute_options @attribute_options ||= {} end def self.memoized(key) memoized_attributes[key.to_sym] = Proc.new class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{key}() self[:#{key}]; end RUBY end def self.memoized_attributes @memoized_attributes ||= {} end def [](key) key = key.to_sym if method = self.class.memoized_attributes[key] super(key) || (self[key] = instance_eval(&method)) else super end end def symbolized_key_set @symbolized_key_set ||= Set.new(keys.map { |k| k.to_sym }) end def self.inherited(subclass) super subclass.attribute_options.update(attribute_options) subclass.memoized_attributes.update(memoized_attributes) end def self.fetch_error_class @fetch_error_class ||= if Object.const_defined?(:KeyError) ::KeyError else ::IndexError end end end class RequestOptions < Options.new(:params_encoder, :proxy, :bind, :timeout, :open_timeout, :boundary, :oauth) def []=(key, value) if key && key.to_sym == :proxy super(key, value ? ProxyOptions.from(value) : nil) else super(key, value) end end end class SSLOptions < Options.new(:verify, :ca_file, :ca_path, :verify_mode, :cert_store, :client_cert, :client_key, :certificate, :private_key, :verify_depth, :version) def verify? verify != false end def disable? !verify? end end class ProxyOptions < Options.new(:uri, :user, :password) extend Forwardable def_delegators :uri, :scheme, :scheme=, :host, :host=, :port, :port=, :path, :path= def self.from(value) case value when String value = {:uri => Utils.URI(value)} when URI value = {:uri => value} when Hash, Options if uri = value.delete(:uri) value[:uri] = Utils.URI(uri) end end super(value) end memoized(:user) { uri.user && Utils.unescape(uri.user) } memoized(:password) { uri.password && Utils.unescape(uri.password) } end class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url, :parallel_manager, :params, :headers, :builder_class) options :request => RequestOptions, :ssl => SSLOptions memoized(:request) { self.class.options_for(:request).new } memoized(:ssl) { self.class.options_for(:ssl).new } memoized(:builder_class) { RackBuilder } def new_builder(block) builder_class.new(&block) end end class Env < Options.new(:method, :body, :url, :request, :request_headers, :ssl, :parallel_manager, :params, :response, :response_headers, :status) ContentLength = 'Content-Length'.freeze StatusesWithoutBody = Set.new [204, 304] SuccessfulStatuses = 200..299 # A Set of HTTP verbs that typically send a body. If no body is set for # these requests, the Content-Length header is set to 0. MethodsWithBodies = Set.new [:post, :put, :patch, :options] options :request => RequestOptions, :request_headers => Utils::Headers, :response_headers => Utils::Headers extend Forwardable def_delegators :request, :params_encoder # Public def self.from(value) env = super(value) if value.respond_to?(:custom_members) env.custom_members.update(value.custom_members) end env end # Public def [](key) if in_member_set?(key) super(key) else custom_members[key] end end # Public def []=(key, value) if in_member_set?(key) super(key, value) else custom_members[key] = value end end # Public def success? SuccessfulStatuses.include?(status) end # Public def needs_body? !body && MethodsWithBodies.include?(method) end # Public def clear_body request_headers[ContentLength] = '0' self.body = '' end # Public def parse_body? !StatusesWithoutBody.include?(status) end # Public def parallel? !!parallel_manager end def inspect attrs = [nil] members.each do |mem| if value = send(mem) attrs << "@#{mem}=#{value.inspect}" end end if !custom_members.empty? attrs << "@custom=#{custom_members.inspect}" end %(#<#{self.class}#{attrs.join(" ")}>) end # Internal def custom_members @custom_members ||= {} end # Internal if members.first.is_a?(Symbol) def in_member_set?(key) self.class.member_set.include?(key.to_sym) end else def in_member_set?(key) self.class.member_set.include?(key.to_s) end end # Internal def self.member_set @member_set ||= Set.new(members) end end end faraday-0.9.2/lib/faraday/parameters.rb000066400000000000000000000132011260477344700200110ustar00rootroot00000000000000require "forwardable" module Faraday module NestedParamsEncoder class << self extend Forwardable def_delegators :'Faraday::Utils', :escape, :unescape end def self.encode(params) return nil if params == nil if !params.is_a?(Array) if !params.respond_to?(:to_hash) raise TypeError, "Can't convert #{params.class} into Hash." end params = params.to_hash params = params.map do |key, value| key = key.to_s if key.kind_of?(Symbol) [key, value] end # Useful default for OAuth and caching. # Only to be used for non-Array inputs. Arrays should preserve order. params.sort! end # Helper lambda to_query = lambda do |parent, value| if value.is_a?(Hash) value = value.map do |key, val| key = escape(key) [key, val] end value.sort! buffer = "" value.each do |key, val| new_parent = "#{parent}%5B#{key}%5D" buffer << "#{to_query.call(new_parent, val)}&" end return buffer.chop elsif value.is_a?(Array) buffer = "" value.each_with_index do |val, i| new_parent = "#{parent}%5B%5D" buffer << "#{to_query.call(new_parent, val)}&" end return buffer.chop elsif value.nil? return parent else encoded_value = escape(value) return "#{parent}=#{encoded_value}" end end # The params have form [['key1', 'value1'], ['key2', 'value2']]. buffer = '' params.each do |parent, value| encoded_parent = escape(parent) buffer << "#{to_query.call(encoded_parent, value)}&" end return buffer.chop end def self.decode(query) return nil if query == nil params = {} query.split("&").each do |pair| next if pair.empty? key, value = pair.split("=", 2) key = unescape(key) value = unescape(value.gsub(/\+/, ' ')) if value subkeys = key.scan(/[^\[\]]+(?:\]?\[\])?/) context = params subkeys.each_with_index do |subkey, i| is_array = subkey =~ /[\[\]]+\Z/ subkey = $` if is_array last_subkey = i == subkeys.length - 1 if !last_subkey || is_array value_type = is_array ? Array : Hash if context[subkey] && !context[subkey].is_a?(value_type) raise TypeError, "expected %s (got %s) for param `%s'" % [ value_type.name, context[subkey].class.name, subkey ] end context = (context[subkey] ||= value_type.new) end if context.is_a?(Array) && !is_array if !context.last.is_a?(Hash) || context.last.has_key?(subkey) context << {} end context = context.last end if last_subkey if is_array context << value else context[subkey] = value end end end end dehash(params, 0) end # Internal: convert a nested hash with purely numeric keys into an array. # FIXME: this is not compatible with Rack::Utils.parse_nested_query def self.dehash(hash, depth) hash.each do |key, value| hash[key] = dehash(value, depth + 1) if value.kind_of?(Hash) end if depth > 0 && !hash.empty? && hash.keys.all? { |k| k =~ /^\d+$/ } hash.keys.sort.inject([]) { |all, key| all << hash[key] } else hash end end end module FlatParamsEncoder class << self extend Forwardable def_delegators :'Faraday::Utils', :escape, :unescape end def self.encode(params) return nil if params == nil if !params.is_a?(Array) if !params.respond_to?(:to_hash) raise TypeError, "Can't convert #{params.class} into Hash." end params = params.to_hash params = params.map do |key, value| key = key.to_s if key.kind_of?(Symbol) [key, value] end # Useful default for OAuth and caching. # Only to be used for non-Array inputs. Arrays should preserve order. params.sort! end # The params have form [['key1', 'value1'], ['key2', 'value2']]. buffer = '' params.each do |key, value| encoded_key = escape(key) value = value.to_s if value == true || value == false if value == nil buffer << "#{encoded_key}&" elsif value.kind_of?(Array) value.each do |sub_value| encoded_value = escape(sub_value) buffer << "#{encoded_key}=#{encoded_value}&" end else encoded_value = escape(value) buffer << "#{encoded_key}=#{encoded_value}&" end end return buffer.chop end def self.decode(query) empty_accumulator = {} return nil if query == nil split_query = (query.split('&').map do |pair| pair.split('=', 2) if pair && !pair.empty? end).compact return split_query.inject(empty_accumulator.dup) do |accu, pair| pair[0] = unescape(pair[0]) pair[1] = true if pair[1].nil? if pair[1].respond_to?(:to_str) pair[1] = unescape(pair[1].to_str.gsub(/\+/, " ")) end if accu[pair[0]].kind_of?(Array) accu[pair[0]] << pair[1] elsif accu[pair[0]] accu[pair[0]] = [accu[pair[0]], pair[1]] else accu[pair[0]] = pair[1] end accu end end end end faraday-0.9.2/lib/faraday/rack_builder.rb000066400000000000000000000141171260477344700203030ustar00rootroot00000000000000module 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 env.response = response response.finish(env) unless env.parallel? 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.params_encoder), 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 faraday-0.9.2/lib/faraday/request.rb000066400000000000000000000056351260477344700173520ustar00rootroot00000000000000module Faraday # Used to setup urls, params, headers, and the request body in a sane manner. # # @connection.post do |req| # req.url 'http://localhost', 'a' => '1' # 'http://localhost?a=1' # req.headers['b'] = '2' # Header # req.params['c'] = '3' # GET Param # req['b'] = '2' # also Header # req.body = 'abc' # end # class Request < Struct.new(:method, :path, :params, :headers, :body, :options) extend MiddlewareRegistry register_middleware File.expand_path('../request', __FILE__), :url_encoded => [:UrlEncoded, 'url_encoded'], :multipart => [:Multipart, 'multipart'], :retry => [:Retry, 'retry'], :authorization => [:Authorization, 'authorization'], :basic_auth => [:BasicAuthentication, 'basic_authentication'], :token_auth => [:TokenAuthentication, 'token_authentication'], :instrumentation => [:Instrumentation, 'instrumentation'] def self.create(request_method) new(request_method).tap do |request| yield(request) if block_given? end end # Public: Replace params, preserving the existing hash type def params=(hash) if params params.replace hash else super end end # Public: Replace request headers, preserving the existing hash type def headers=(hash) if headers headers.replace hash else super end end def url(path, params = nil) if path.respond_to? :query if query = path.query path = path.dup path.query = nil end else path, query = path.split('?', 2) end self.path = path self.params.merge_query query, options.params_encoder self.params.update(params) if params end def [](key) headers[key] end def []=(key, value) headers[key] = value 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 to_env(connection) Env.new(method, body, connection.build_exclusive_url(path, params), options, headers, connection.ssl, connection.parallel_manager) end end end faraday-0.9.2/lib/faraday/request/000077500000000000000000000000001260477344700170145ustar00rootroot00000000000000faraday-0.9.2/lib/faraday/request/authorization.rb000066400000000000000000000017241260477344700222450ustar00rootroot00000000000000module Faraday class Request::Authorization < Faraday::Middleware KEY = "Authorization".freeze unless defined? KEY # Public def self.header(type, token) case token when String, Symbol "#{type} #{token}" when Hash build_hash(type.to_s, token) else raise ArgumentError, "Can't build an Authorization #{type} header from #{token.inspect}" end end # Internal def self.build_hash(type, hash) offset = KEY.size + type.size + 3 comma = ",\n#{' ' * offset}" values = [] hash.each do |key, value| values << "#{key}=#{value.to_s.inspect}" end "#{type} #{values * comma}" end def initialize(app, type, token) @header_value = self.class.header(type, token) super(app) end # Public def call(env) unless env.request_headers[KEY] env.request_headers[KEY] = @header_value end @app.call(env) end end end faraday-0.9.2/lib/faraday/request/basic_authentication.rb000066400000000000000000000004371260477344700235250ustar00rootroot00000000000000require 'base64' module Faraday class Request::BasicAuthentication < Request.load_middleware(:authorization) # Public def self.header(login, pass) value = Base64.encode64([login, pass].join(':')) value.gsub!("\n", '') super(:Basic, value) end end end faraday-0.9.2/lib/faraday/request/instrumentation.rb000066400000000000000000000020101260477344700225750ustar00rootroot00000000000000module Faraday class Request::Instrumentation < Faraday::Middleware class Options < Faraday::Options.new(:name, :instrumenter) def name self[:name] ||= 'request.faraday' end def instrumenter self[:instrumenter] ||= ActiveSupport::Notifications end end # Public: Instruments requests using Active Support. # # Measures time spent only for synchronous requests. # # Examples # # ActiveSupport::Notifications.subscribe('request.faraday') do |name, starts, ends, _, env| # url = env[:url] # http_method = env[:method].to_s.upcase # duration = ends - starts # $stderr.puts '[%s] %s %s (%.3f s)' % [url.host, http_method, url.request_uri, duration] # end def initialize(app, options = nil) super(app) @name, @instrumenter = Options.from(options).values_at(:name, :instrumenter) end def call(env) @instrumenter.instrument(@name, env) do @app.call(env) end end end end faraday-0.9.2/lib/faraday/request/multipart.rb000066400000000000000000000037101260477344700213630ustar00rootroot00000000000000require File.expand_path("../url_encoded", __FILE__) module Faraday class Request::Multipart < Request::UrlEncoded self.mime_type = 'multipart/form-data'.freeze DEFAULT_BOUNDARY = "-----------RubyMultipartPost".freeze unless defined? DEFAULT_BOUNDARY def call(env) match_content_type(env) do |params| env.request.boundary ||= DEFAULT_BOUNDARY env.request_headers[CONTENT_TYPE] += "; boundary=#{env.request.boundary}" env.body = create_multipart(env, params) end @app.call env end def process_request?(env) type = request_type(env) env.body.respond_to?(:each_key) and !env.body.empty? and ( (type.empty? and has_multipart?(env.body)) or type == self.class.mime_type ) end def has_multipart?(obj) # string is an enum in 1.8, returning list of itself if obj.respond_to?(:each) && !obj.is_a?(String) (obj.respond_to?(:values) ? obj.values : obj).each do |val| return true if (val.respond_to?(:content_type) || has_multipart?(val)) end end false end def create_multipart(env, params) boundary = env.request.boundary parts = process_params(params) do |key, value| Faraday::Parts::Part.new(boundary, key, value) end parts << Faraday::Parts::EpiloguePart.new(boundary) body = Faraday::CompositeReadIO.new(parts) env.request_headers[Faraday::Env::ContentLength] = body.length.to_s return body end def process_params(params, prefix = nil, pieces = nil, &block) params.inject(pieces || []) do |all, (key, value)| key = "#{prefix}[#{key}]" if prefix case value when Array values = value.inject([]) { |a,v| a << [nil, v] } process_params(values, key, all, &block) when Hash process_params(value, key, all, &block) else all << block.call(key, value) end end end end end faraday-0.9.2/lib/faraday/request/retry.rb000066400000000000000000000124271260477344700205140ustar00rootroot00000000000000module Faraday # Catches exceptions and retries each request a limited number of times. # # By default, it retries 2 times and handles only timeout exceptions. It can # be configured with an arbitrary number of retries, a list of exceptions to # handle, a retry interval, a percentage of randomness to add to the retry # interval, and a backoff factor. # # Examples # # Faraday.new do |conn| # conn.request :retry, max: 2, interval: 0.05, # interval_randomness: 0.5, backoff_factor: 2 # exceptions: [CustomException, 'Timeout::Error'] # conn.adapter ... # end # # This example will result in a first interval that is random between 0.05 and 0.075 and a second # interval that is random between 0.1 and 0.15 # class Request::Retry < Faraday::Middleware IDEMPOTENT_METHODS = [:delete, :get, :head, :options, :put] class Options < Faraday::Options.new(:max, :interval, :max_interval, :interval_randomness, :backoff_factor, :exceptions, :methods, :retry_if) DEFAULT_CHECK = lambda { |env,exception| false } def self.from(value) if Fixnum === value new(value) else super(value) end end def max (self[:max] ||= 2).to_i end def interval (self[:interval] ||= 0).to_f end def max_interval (self[:max_interval] ||= Float::MAX).to_f end def interval_randomness (self[:interval_randomness] ||= 0).to_f end def backoff_factor (self[:backoff_factor] ||= 1).to_f end def exceptions Array(self[:exceptions] ||= [Errno::ETIMEDOUT, 'Timeout::Error', Error::TimeoutError]) end def methods Array(self[:methods] ||= IDEMPOTENT_METHODS) end def retry_if self[:retry_if] ||= DEFAULT_CHECK end end # Public: Initialize middleware # # Options: # max - Maximum number of retries (default: 2) # interval - Pause in seconds between retries (default: 0) # interval_randomness - The maximum random interval amount expressed # as a float between 0 and 1 to use in addition to the # interval. (default: 0) # max_interval - An upper limit for the interval (default: Float::MAX) # backoff_factor - The amount to multiple each successive retry's # interval amount by in order to provide backoff # (default: 1) # exceptions - The list of exceptions to handle. Exceptions can be # given as Class, Module, or String. (default: # [Errno::ETIMEDOUT, Timeout::Error, # Error::TimeoutError]) # methods - A list of HTTP methods to retry without calling retry_if. Pass # an empty Array to call retry_if for all exceptions. # (defaults to the idempotent HTTP methods in IDEMPOTENT_METHODS) # retry_if - block that will receive the env object and the exception raised # and should decide if the code should retry still the action or # not independent of the retry count. This would be useful # if the exception produced is non-recoverable or if the # the HTTP method called is not idempotent. # (defaults to return false) def initialize(app, options = nil) super(app) @options = Options.from(options) @errmatch = build_exception_matcher(@options.exceptions) end def sleep_amount(retries) retry_index = @options.max - retries current_interval = @options.interval * (@options.backoff_factor ** retry_index) current_interval = [current_interval, @options.max_interval].min random_interval = rand * @options.interval_randomness.to_f * @options.interval current_interval + random_interval end def call(env) retries = @options.max request_body = env[:body] begin env[:body] = request_body # after failure env[:body] is set to the response body @app.call(env) rescue @errmatch => exception if retries > 0 && retry_request?(env, exception) retries -= 1 sleep sleep_amount(retries + 1) retry end raise end end # Private: construct an exception matcher object. # # An exception matcher for the rescue clause can usually be any object that # responds to `===`, but for Ruby 1.8 it has to be a Class or Module. def build_exception_matcher(exceptions) matcher = Module.new (class << matcher; self; end).class_eval do define_method(:===) do |error| exceptions.any? do |ex| if ex.is_a? Module error.is_a? ex else error.class.to_s == ex.to_s end end end end matcher end private def retry_request?(env, exception) @options.methods.include?(env[:method]) || @options.retry_if.call(env, exception) end end end faraday-0.9.2/lib/faraday/request/token_authentication.rb000066400000000000000000000005201260477344700235550ustar00rootroot00000000000000module Faraday class Request::TokenAuthentication < Request.load_middleware(:authorization) # Public def self.header(token, options = nil) options ||= {} options[:token] = token super(:Token, options) end def initialize(app, token, options = nil) super(app, token, options) end end end faraday-0.9.2/lib/faraday/request/url_encoded.rb000066400000000000000000000017111260477344700216240ustar00rootroot00000000000000module Faraday class Request::UrlEncoded < Faraday::Middleware CONTENT_TYPE = 'Content-Type'.freeze unless defined? CONTENT_TYPE class << self attr_accessor :mime_type end self.mime_type = 'application/x-www-form-urlencoded'.freeze def call(env) match_content_type(env) do |data| params = Faraday::Utils::ParamsHash[data] env.body = params.to_query(env.params_encoder) end @app.call env end def match_content_type(env) if process_request?(env) env.request_headers[CONTENT_TYPE] ||= self.class.mime_type yield(env.body) unless env.body.respond_to?(:to_str) end end def process_request?(env) type = request_type(env) env.body and (type.empty? or type == self.class.mime_type) end def request_type(env) type = env.request_headers[CONTENT_TYPE].to_s type = type.split(';', 2).first if type.index(';') type end end end faraday-0.9.2/lib/faraday/response.rb000066400000000000000000000042301260477344700175060ustar00rootroot00000000000000require 'forwardable' module Faraday class Response # Used for simple response middleware. class Middleware < Faraday::Middleware def call(env) @app.call(env).on_complete do |environment| on_complete(environment) end end # Override this to modify the environment after the response has finished. # Calls the `parse` method if defined def on_complete(env) env.body = parse(env.body) if respond_to?(:parse) && env.parse_body? end end extend Forwardable extend MiddlewareRegistry register_middleware File.expand_path('../response', __FILE__), :raise_error => [:RaiseError, 'raise_error'], :logger => [:Logger, 'logger'] def initialize(env = nil) @env = Env.from(env) if env @on_complete_callbacks = [] end attr_reader :env def_delegators :env, :to_hash def status finished? ? env.status : nil end def headers finished? ? env.response_headers : {} end def_delegator :headers, :[] def body finished? ? env.body : nil end def finished? !!env end def on_complete if not finished? @on_complete_callbacks << Proc.new else yield(env) end return self end def finish(env) raise "response already finished" if finished? @env = env.is_a?(Env) ? env : Env.from(env) @on_complete_callbacks.each { |callback| callback.call(@env) } return self end def success? finished? && env.success? end # because @on_complete_callbacks cannot be marshalled def marshal_dump !finished? ? nil : { :status => @env.status, :body => @env.body, :response_headers => @env.response_headers } end def marshal_load(env) @env = Env.from(env) end # Expand the env with more properties, without overriding existing ones. # Useful for applying request params after restoring a marshalled Response. def apply_request(request_env) raise "response didn't finish yet" unless finished? @env = Env.from(request_env).update(@env) return self end end end faraday-0.9.2/lib/faraday/response/000077500000000000000000000000001260477344700171625ustar00rootroot00000000000000faraday-0.9.2/lib/faraday/response/logger.rb000066400000000000000000000026211260477344700207670ustar00rootroot00000000000000require 'forwardable' module Faraday class Response::Logger < Response::Middleware extend Forwardable DEFAULT_OPTIONS = { :bodies => false } def initialize(app, logger = nil, options = {}) super(app) @logger = logger || begin require 'logger' ::Logger.new(STDOUT) end @options = DEFAULT_OPTIONS.merge(options) end def_delegators :@logger, :debug, :info, :warn, :error, :fatal def call(env) info "#{env.method} #{env.url.to_s}" debug('request') { dump_headers env.request_headers } debug('request') { dump_body(env[:body]) } if env[:body] && log_body?(:request) super end def on_complete(env) info('Status') { env.status.to_s } debug('response') { dump_headers env.response_headers } debug('response') { dump_body env[:body] } if env[:body] && log_body?(:response) end private def dump_headers(headers) headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n") end def dump_body(body) if body.respond_to?(:to_str) body.to_str else pretty_inspect(body) end end def pretty_inspect(body) require 'pp' unless body.respond_to?(:pretty_inspect) body.pretty_inspect end def log_body?(type) case @options[:bodies] when Hash then @options[:bodies][type] else @options[:bodies] end end end end faraday-0.9.2/lib/faraday/response/raise_error.rb000066400000000000000000000012201260477344700220160ustar00rootroot00000000000000module Faraday class Response::RaiseError < Response::Middleware ClientErrorStatuses = 400...600 def on_complete(env) case env[:status] when 404 raise Faraday::Error::ResourceNotFound, response_values(env) when 407 # mimic the behavior that we get with proxy requests with HTTPS raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "} when ClientErrorStatuses raise Faraday::Error::ClientError, response_values(env) end end def response_values(env) {:status => env.status, :headers => env.response_headers, :body => env.body} end end end faraday-0.9.2/lib/faraday/upload_io.rb000066400000000000000000000026001260477344700176220ustar00rootroot00000000000000begin require 'composite_io' require 'parts' require 'stringio' rescue LoadError $stderr.puts "Install the multipart-post gem." raise end module Faraday # Similar but not compatible with ::CompositeReadIO provided by multipart-post. class CompositeReadIO def initialize(*parts) @parts = parts.flatten @ios = @parts.map { |part| part.to_io } @index = 0 end def length @parts.inject(0) { |sum, part| sum + part.length } end def rewind @ios.each { |io| io.rewind } @index = 0 end # Read from IOs in order until `length` bytes have been received. def read(length = nil, outbuf = nil) got_result = false outbuf = outbuf ? outbuf.replace("") : "" while io = current_io if result = io.read(length) got_result ||= !result.nil? result.force_encoding("BINARY") if result.respond_to?(:force_encoding) outbuf << result length -= result.length if length break if length == 0 end advance_io end (!got_result && length) ? nil : outbuf end def close @ios.each { |io| io.close } end def ensure_open_and_readable # Rubinius compatibility end private def current_io @ios[@index] end def advance_io @index += 1 end end UploadIO = ::UploadIO Parts = ::Parts end faraday-0.9.2/lib/faraday/utils.rb000066400000000000000000000163211260477344700170140ustar00rootroot00000000000000require 'thread' module Faraday module Utils extend self # Adapted from Rack::Utils::HeaderHash class Headers < ::Hash def self.from(value) new(value) end def initialize(hash = nil) super() @names = {} self.update(hash || {}) end # on dup/clone, we need to duplicate @names hash def initialize_copy(other) super @names = other.names.dup end # need to synchronize concurrent writes to the shared KeyMap keymap_mutex = Mutex.new # symbol -> string mapper + cache KeyMap = Hash.new do |map, key| value = if key.respond_to?(:to_str) key else key.to_s.split('_'). # :user_agent => %w(user agent) each { |w| w.capitalize! }. # => %w(User Agent) join('-') # => "User-Agent" end keymap_mutex.synchronize { map[key] = value } end KeyMap[:etag] = "ETag" def [](k) k = KeyMap[k] super(k) || super(@names[k.downcase]) end def []=(k, v) k = KeyMap[k] k = (@names[k.downcase] ||= k) # join multiple values with a comma v = v.to_ary.join(', ') if v.respond_to? :to_ary super(k, v) end def fetch(k, *args, &block) k = KeyMap[k] key = @names.fetch(k.downcase, k) super(key, *args, &block) end def delete(k) k = KeyMap[k] if k = @names[k.downcase] @names.delete k.downcase super(k) end end def include?(k) @names.include? k.downcase end alias_method :has_key?, :include? alias_method :member?, :include? alias_method :key?, :include? def merge!(other) other.each { |k, v| self[k] = v } self end alias_method :update, :merge! def merge(other) hash = dup hash.merge! other end def replace(other) clear @names.clear self.update other self end def to_hash() ::Hash.new.update(self) end def parse(header_string) return unless header_string && !header_string.empty? header_string.split(/\r\n/). tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line map { |h| h.split(/:\s+/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines each { |key, value| # join multiple values with a comma if self[key] self[key] << ', ' << value else self[key] = value end } end protected def names @names end end # hash with stringified keys class ParamsHash < Hash def [](key) super(convert_key(key)) end def []=(key, value) super(convert_key(key), value) end def delete(key) super(convert_key(key)) end def include?(key) super(convert_key(key)) end alias_method :has_key?, :include? alias_method :member?, :include? alias_method :key?, :include? def update(params) params.each do |key, value| self[key] = value end self end alias_method :merge!, :update def merge(params) dup.update(params) end def replace(other) clear update(other) end def merge_query(query, encoder = nil) if query && !query.empty? update((encoder || Utils.default_params_encoder).decode(query)) end self end def to_query(encoder = nil) (encoder || Utils.default_params_encoder).encode(self) end private def convert_key(key) key.to_s end end def build_query(params) FlatParamsEncoder.encode(params) end def build_nested_query(params) NestedParamsEncoder.encode(params) end ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/ def escape(s) s.to_s.gsub(ESCAPE_RE) {|match| '%' + match.unpack('H2' * match.bytesize).join('%').upcase }.tr(' ', '+') end def unescape(s) CGI.unescape s.to_s end DEFAULT_SEP = /[&;] */n # Adapted from Rack def parse_query(query) FlatParamsEncoder.decode(query) end def parse_nested_query(query) NestedParamsEncoder.decode(query) end def default_params_encoder @default_params_encoder ||= NestedParamsEncoder end class << self attr_writer :default_params_encoder end # Stolen from Rack def normalize_params(params, name, v = nil) name =~ %r(\A[\[\]]*([^\[\]]+)\]*) k = $1 || '' after = $' || '' return if k.empty? if after == "" if params[k] params[k] = Array[params[k]] unless params[k].kind_of?(Array) params[k] << v else params[k] = v end elsif after == "[]" params[k] ||= [] raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) params[k] << v elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) child_key = $1 params[k] ||= [] raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) normalize_params(params[k].last, child_key, v) else params[k] << normalize_params({}, child_key, v) end else params[k] ||= {} raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) params[k] = normalize_params(params[k], after, v) end return params end # Normalize URI() behavior across Ruby versions # # url - A String or URI. # # Returns a parsed URI. def URI(url) if url.respond_to?(:host) url elsif url.respond_to?(:to_str) default_uri_parser.call(url) else raise ArgumentError, "bad argument (expected URI object or URI string)" end end def default_uri_parser @default_uri_parser ||= begin require 'uri' Kernel.method(:URI) end end def default_uri_parser=(parser) @default_uri_parser = if parser.respond_to?(:call) || parser.nil? parser else parser.method(:parse) end end # Receives a String or URI and returns just the path with the query string sorted. def normalize_path(url) url = URI(url) (url.path.start_with?('/') ? url.path : '/' + url.path) + (url.query ? "?#{sort_query_params(url.query)}" : "") end # Recursive hash update def deep_merge!(target, hash) hash.each do |key, value| if Hash === value and Hash === target[key] target[key] = deep_merge(target[key], value) else target[key] = value end end target end # Recursive hash merge def deep_merge(source, hash) deep_merge!(source.dup, hash) end protected def sort_query_params(query) query.split('&').sort.join('&') end end end faraday-0.9.2/script/000077500000000000000000000000001260477344700144535ustar00rootroot00000000000000faraday-0.9.2/script/console000077500000000000000000000002611260477344700160420ustar00rootroot00000000000000#!/usr/bin/env bash # Usage: script/console # Starts an IRB console with this library loaded. gemspec="$(ls *.gemspec | head -1)" exec bundle exec irb -r "${gemspec%.*}" "$@" faraday-0.9.2/script/generate_certs000077500000000000000000000025131260477344700173740ustar00rootroot00000000000000#!/usr/bin/env ruby # Usage: generate_certs # Generate test certs for testing Faraday with SSL require 'openssl' require 'fileutils' $shell = ARGV.include? '-s' # Adapted from WEBrick::Utils. Skips cert extensions so it # can be used as a CA bundle def create_self_signed_cert(bits, cn, comment) rsa = OpenSSL::PKey::RSA.new(bits) cert = OpenSSL::X509::Certificate.new cert.version = 2 cert.serial = 1 name = OpenSSL::X509::Name.new(cn) cert.subject = name cert.issuer = name cert.not_before = Time.now cert.not_after = Time.now + (365*24*60*60) cert.public_key = rsa.public_key cert.sign(rsa, OpenSSL::Digest::SHA1.new) return [cert, rsa] end def write(file, contents, env_var) FileUtils.mkdir_p(File.dirname(file)) File.open(file, 'w') {|f| f.puts(contents) } puts %(export #{env_var}="#{file}") if $shell end # One cert / CA for ease of testing when ignoring verification cert, key = create_self_signed_cert(1024, [['CN', 'localhost']], 'Faraday Test CA') write 'tmp/faraday-cert.key', key, 'SSL_KEY' write 'tmp/faraday-cert.crt', cert, 'SSL_FILE' # And a second CA to prove that verification can fail cert, key = create_self_signed_cert(1024, [['CN', 'real-ca.com']], 'A different CA') write 'tmp/faraday-different-ca-cert.key', key, 'SSL_KEY_ALT' write 'tmp/faraday-different-ca-cert.crt', cert, 'SSL_FILE_ALT' faraday-0.9.2/script/package000077500000000000000000000002301260477344700157670ustar00rootroot00000000000000#!/usr/bin/env bash # Usage: script/gem # Updates the gemspec and builds a new gem in the pkg directory. mkdir -p pkg gem build *.gemspec mv *.gem pkg faraday-0.9.2/script/proxy-server000077500000000000000000000021551260477344700170710ustar00rootroot00000000000000#!/usr/bin/env ruby # Usage: script/proxy-server [-p PORT] [-u USER:PASSWORD] require 'webrick' require 'webrick/httpproxy' port = 4001 if found = ARGV.index('-p') port = ARGV[found + 1].to_i end if found = ARGV.index('-u') username, password = ARGV[found + 1].split(':', 2) end match_credentials = lambda { |credentials| got_username, got_password = credentials.to_s.unpack("m*")[0].split(":", 2) got_username == username && got_password == password } log_io = $stdout log_io.sync = true webrick_opts = { :Port => port, :Logger => WEBrick::Log::new(log_io), :AccessLog => [[log_io, "[%{X-Faraday-Adapter}i] %m %U -> %s %b"]], :ProxyAuthProc => lambda { |req, res| if username type, credentials = req.header['proxy-authorization'].first.to_s.split(/\s+/, 2) unless "Basic" == type && match_credentials.call(credentials) res['proxy-authenticate'] = %{Basic realm="testing"} raise WEBrick::HTTPStatus::ProxyAuthenticationRequired end end } } proxy = WEBrick::HTTPProxyServer.new(webrick_opts) trap(:TERM) { proxy.shutdown } trap(:INT) { proxy.shutdown } proxy.start faraday-0.9.2/script/release000077500000000000000000000005531260477344700160240ustar00rootroot00000000000000#!/usr/bin/env bash # Usage: script/release # Build the package, tag a commit, push it to origin, and then release the # package publicly. set -e version="$(script/package | grep Version: | awk '{print $2}')" [ -n "$version" ] || exit 1 git commit -a -m "faraday $version" git tag "v${version}" git push origin "v${version}" HEAD gem push pkg/*-${version}.gem faraday-0.9.2/script/server000077500000000000000000000016171260477344700157140ustar00rootroot00000000000000#!/usr/bin/env ruby old_verbose, $VERBOSE = $VERBOSE, nil begin require File.expand_path('../../test/live_server', __FILE__) ensure $VERBOSE = old_verbose end require 'webrick' port = 4000 if found = ARGV.index('-p') port = ARGV[found + 1].to_i end log_io = $stdout log_io.sync = true webrick_opts = { :Port => port, :Logger => WEBrick::Log::new(log_io), :AccessLog => [[log_io, "[%{X-Faraday-Adapter}i] %m %U -> %s %b"]] } if ENV['SSL_KEY'] require 'openssl' require 'webrick/https' webrick_opts.update \ :SSLEnable => true, :SSLPrivateKey => OpenSSL::PKey::RSA.new(File.read(ENV['SSL_KEY'])), :SSLCertificate => OpenSSL::X509::Certificate.new(File.read(ENV['SSL_FILE'])), :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE end Rack::Handler::WEBrick.run(Faraday::LiveServer, webrick_opts) do |server| trap(:INT) { server.stop } trap(:TERM) { server.stop } end faraday-0.9.2/script/test000077500000000000000000000103161260477344700153610ustar00rootroot00000000000000#!/usr/bin/env bash # Usage: script/test [file] [adapter]... -- [test/unit options] # Runs the test suite against a local server spawned automatically in a # thread. After tests are done, the server is shut down. # # If filename arguments are given, only those files are run. If arguments given # are not filenames, they are taken as words that filter the list of files to run. # # Examples: # # $ script/test # $ script/test test/env_test.rb # $ script/test excon typhoeus # # # Run only tests matching /ssl/ for the net_http adapter, with SSL enabled. # $ SSL=yes script/test net_http -- -n /ssl/ # # # Run against multiple rbenv versions # $ RBENV_VERSIONS="1.9.3-p194 ree-1.8.7-2012.02" script/test set -e if [[ "$RUBYOPT" != *"bundler/setup"* ]]; then export RUBYOPT="-rbundler/setup $RUBYOPT" fi port=3999 proxy_port=3998 scheme=http if [ "$SSL" = "yes" ]; then scheme=https if [ -z "$SSL_KEY" ] || [ -z "$SSL_FILE" ]; then eval "$(script/generate_certs -s)" fi fi find_test_files() { find "$1" -name '*_test.rb' } filter_matching() { pattern="$1" shift for line in "$@"; do [[ $line == *"$pattern"* ]] && echo "$line" done } start_server() { mkdir -p log script/server -p $port >log/test.log 2>&1 & echo $! } start_proxy() { mkdir -p log script/proxy-server -p $proxy_port -u "faraday@test.local:there is cake" >log/proxy.log 2>&1 & echo $! } server_started() { lsof -i :${1?} >/dev/null } timestamp() { date +%s } wait_for_server() { timeout=$(( `timestamp` + $1 )) while true; do if server_started "$2"; then break elif [ `timestamp` -gt "$timeout" ]; then echo "timed out after $1 seconds" >&2 return 1 fi done } filtered= IFS=$'\n' test_files=($(find_test_files "test")) declare -a explicit_files # Process filter arguments: # - test filenames as taken as-is # - other words are taken as pattern to match the list of known files against # - arguments after "--" are forwarded to the ruby process while [ $# -gt 0 ]; do arg="$1" shift if [ "$arg" = "--" ]; then break elif [ -f "$arg" ]; then filtered=true explicit_files[${#explicit_files[@]}+1]="$arg" else filtered=true IFS=$'\n' explicit_files=( ${explicit_files[@]} $(filter_matching "$arg" "${test_files[@]}" || true) ) fi done # If there were filter args, replace test files list with the results if [ -n "$filtered" ]; then if [ ${#explicit_files[@]} -eq 0 ]; then echo "Error: no test files match" >&2 exit 1 else test_files=(${explicit_files[@]}) echo running "${test_files[@]}" fi fi # If there are any adapter tests, spin up the HTTP server if [ -n "$(filter_matching "adapters" "${test_files[@]}")" ]; then if server_started $port; then echo "aborted: another instance of server running on $port" >&2 exit 1 fi server_pid=$(start_server) proxy_pid=$(start_proxy) wait_for_server 30 $port || { cat log/test.log kill "$server_pid" kill "$proxy_pid" exit 1 } wait_for_server 5 $proxy_port cleanup() { if [ $? -ne 0 ] && [ -n "$TRAVIS" ]; then cat log/test.log cat log/proxy.log fi kill "$server_pid" kill "$proxy_pid" } trap cleanup INT EXIT export LIVE="${scheme}://localhost:${port}" export LIVE_PROXY="http://faraday%40test.local:there%20is%20cake@localhost:${proxy_port}" fi warnings="${TMPDIR:-/tmp}/faraday-warnings.$$" run_test_files() { # Save warnings on stderr to a separate file RUBYOPT="$RUBYOPT -w" ruby -e 'while f=ARGV.shift and f!="--"; load f; end' "${test_files[@]}" -- "$@" \ 2> >(tee >(grep 'warning:' >"$warnings") | grep -v 'warning:') } check_warnings() { # Display Ruby warnings from this project's source files. Abort if any were found. num="$(grep -F "$PWD" "$warnings" | grep -v "${PWD}/vendor/bundle" | sort | uniq -c | sort -rn | tee /dev/stderr | wc -l)" rm -f "$warnings" if [ "$num" -gt 0 ]; then echo "FAILED: this test suite doesn't tolerate Ruby syntax warnings!" >&2 exit 1 fi } if [ -n "$RBENV_VERSIONS" ]; then IFS=' ' versions=($RBENV_VERSIONS) for version in "${versions[@]}"; do echo "[${version}]" RBENV_VERSION="$version" run_test_files "$@" done else run_test_files "$@" fi check_warnings faraday-0.9.2/test/000077500000000000000000000000001260477344700141265ustar00rootroot00000000000000faraday-0.9.2/test/adapters/000077500000000000000000000000001260477344700157315ustar00rootroot00000000000000faraday-0.9.2/test/adapters/default_test.rb000066400000000000000000000004511260477344700207410ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class DefaultTest < Faraday::TestCase def adapter() :default end Integration.apply(self, :NonParallel) do # default stack is not configured with Multipart undef :test_POST_sends_files end end end faraday-0.9.2/test/adapters/em_http_test.rb000066400000000000000000000011041260477344700207510ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class EMHttpTest < Faraday::TestCase def adapter() :em_http end Integration.apply(self, :Parallel) do # https://github.com/eventmachine/eventmachine/pull/289 undef :test_timeout def test_binds_local_socket host = '1.2.3.4' conn = create_connection :request => { :bind => { :host => host } } assert_equal host, conn.options[:bind][:host] end end unless jruby? and ssl_mode? # https://github.com/eventmachine/eventmachine/issues/180 end end faraday-0.9.2/test/adapters/em_synchrony_test.rb000066400000000000000000000011021260477344700220240ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class EMSynchronyTest < Faraday::TestCase def adapter() :em_synchrony end Integration.apply(self, :Parallel) do # https://github.com/eventmachine/eventmachine/pull/289 undef :test_timeout def test_binds_local_socket host = '1.2.3.4' conn = create_connection :request => { :bind => { :host => host } } #put conn.get('/who-am-i').body assert_equal host, conn.options[:bind][:host] end end unless RUBY_VERSION < '1.9' or jruby? end end faraday-0.9.2/test/adapters/excon_test.rb000066400000000000000000000011271260477344700204320ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class ExconTest < Faraday::TestCase def adapter() :excon end Integration.apply(self, :NonParallel) do # https://github.com/geemus/excon/issues/126 ? undef :test_timeout if ssl_mode? # Excon lets OpenSSL::SSL::SSLError be raised without any way to # distinguish whether it happened because of a 407 proxy response undef :test_proxy_auth_fail if ssl_mode? # https://github.com/geemus/excon/issues/358 undef :test_connection_error if RUBY_VERSION >= '2.1.0' end end end faraday-0.9.2/test/adapters/httpclient_test.rb000066400000000000000000000010641260477344700214740ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class HttpclientTest < Faraday::TestCase def adapter() :httpclient end Integration.apply(self, :NonParallel, :Compression) do def setup require 'httpclient' unless defined?(HTTPClient) HTTPClient::NO_PROXY_HOSTS.delete('localhost') end def test_binds_local_socket host = '1.2.3.4' conn = create_connection :request => { :bind => { :host => host } } assert_equal host, conn.options[:bind][:host] end end end end faraday-0.9.2/test/adapters/integration.rb000066400000000000000000000177771260477344700206240ustar00rootroot00000000000000require 'forwardable' require File.expand_path("../../helper", __FILE__) Faraday.require_lib 'autoload' module Adapters # Adapter integration tests. To use, implement two methods: # # `#adapter` required. returns a symbol for the adapter middleware name # `#adapter_options` optional. extra arguments for building an adapter module Integration def self.apply(base, *extra_features) if base.live_server? features = [:Common] features.concat extra_features features << :SSL if base.ssl_mode? features.each {|name| base.send(:include, self.const_get(name)) } yield if block_given? elsif !defined? @warned warn "Warning: Not running integration tests against a live server." warn "Start the server `ruby test/live_server.rb` and set the LIVE=1 env variable." @warned = true end end module Parallel def test_in_parallel resp1, resp2 = nil, nil connection = create_connection connection.in_parallel do resp1 = connection.get('echo?a=1') resp2 = connection.get('echo?b=2') assert connection.in_parallel? assert_nil resp1.body assert_nil resp2.body end assert !connection.in_parallel? assert_equal 'get ?{"a"=>"1"}', resp1.body assert_equal 'get ?{"b"=>"2"}', resp2.body end end module NonParallel def test_no_parallel_support connection = create_connection response = nil err = capture_warnings do connection.in_parallel do response = connection.get('echo').body end end assert response assert_match "no parallel-capable adapter on Faraday stack", err assert_match __FILE__, err end end module Compression def test_GET_handles_compression res = get('echo_header', :name => 'accept-encoding') assert_match(/\bgzip\b/, res.body) assert_match(/\bdeflate\b/, res.body) end end module SSL def test_GET_ssl_fails_with_bad_cert ca_file = 'tmp/faraday-different-ca-cert.crt' conn = create_connection(:ssl => {:ca_file => ca_file}) err = assert_raises Faraday::SSLError do conn.get('/ssl') end assert_includes err.message, "certificate" end end module Common extend Forwardable def_delegators :create_connection, :get, :head, :put, :post, :patch, :delete, :run_request def test_GET_retrieves_the_response_body assert_equal 'get', get('echo').body end def test_GET_send_url_encoded_params assert_equal %(get ?{"name"=>"zack"}), get('echo', :name => 'zack').body end def test_GET_retrieves_the_response_headers response = get('echo') assert_match(/text\/plain/, response.headers['Content-Type'], 'original case fail') assert_match(/text\/plain/, response.headers['content-type'], 'lowercase fail') end def test_GET_handles_headers_with_multiple_values assert_equal 'one, two', get('multi').headers['set-cookie'] end def test_GET_with_body response = get('echo') do |req| req.body = {'bodyrock' => true} end assert_equal %(get {"bodyrock"=>"true"}), response.body end def test_GET_sends_user_agent response = get('echo_header', {:name => 'user-agent'}, :user_agent => 'Agent Faraday') assert_equal 'Agent Faraday', response.body end def test_GET_ssl expected = self.class.ssl_mode?.to_s assert_equal expected, get('ssl').body end def test_POST_send_url_encoded_params assert_equal %(post {"name"=>"zack"}), post('echo', :name => 'zack').body end def test_POST_send_url_encoded_nested_params resp = post('echo', 'name' => {'first' => 'zack'}) assert_equal %(post {"name"=>{"first"=>"zack"}}), resp.body end def test_POST_retrieves_the_response_headers assert_match(/text\/plain/, post('echo').headers['content-type']) end def test_POST_sends_files resp = post('file') do |req| req.body = {'uploaded_file' => Faraday::UploadIO.new(__FILE__, 'text/x-ruby')} end assert_equal "file integration.rb text/x-ruby #{File.size(__FILE__)}", resp.body end def test_PUT_send_url_encoded_params assert_equal %(put {"name"=>"zack"}), put('echo', :name => 'zack').body end def test_PUT_send_url_encoded_nested_params resp = put('echo', 'name' => {'first' => 'zack'}) assert_equal %(put {"name"=>{"first"=>"zack"}}), resp.body end def test_PUT_retrieves_the_response_headers assert_match(/text\/plain/, put('echo').headers['content-type']) end def test_PATCH_send_url_encoded_params assert_equal %(patch {"name"=>"zack"}), patch('echo', :name => 'zack').body end def test_OPTIONS resp = run_request(:options, 'echo', nil, {}) assert_equal 'options', resp.body end def test_HEAD_retrieves_no_response_body assert_equal '', head('echo').body end def test_HEAD_retrieves_the_response_headers assert_match(/text\/plain/, head('echo').headers['content-type']) end def test_DELETE_retrieves_the_response_headers assert_match(/text\/plain/, delete('echo').headers['content-type']) end def test_DELETE_retrieves_the_body assert_equal %(delete), delete('echo').body end def test_timeout conn = create_connection(:request => {:timeout => 1, :open_timeout => 1}) assert_raises Faraday::Error::TimeoutError do conn.get '/slow' end end def test_connection_error assert_raises Faraday::Error::ConnectionFailed do get 'http://localhost:4' end end def test_proxy proxy_uri = URI(ENV['LIVE_PROXY']) conn = create_connection(:proxy => proxy_uri) res = conn.get '/echo' assert_equal 'get', res.body unless self.class.ssl_mode? # proxy can't append "Via" header for HTTPS responses assert_match(/:#{proxy_uri.port}$/, res['via']) end end def test_proxy_auth_fail proxy_uri = URI(ENV['LIVE_PROXY']) proxy_uri.password = 'WRONG' conn = create_connection(:proxy => proxy_uri) err = assert_raises Faraday::Error::ConnectionFailed do conn.get '/echo' end unless self.class.ssl_mode? && (self.class.jruby? || adapter == :em_http || adapter == :em_synchrony) # JRuby raises "End of file reached" which cannot be distinguished from a 407 # EM raises "connection closed by server" due to https://github.com/igrigorik/em-socksify/pull/19 assert_equal %{407 "Proxy Authentication Required "}, err.message end end def test_empty_body_response_represented_as_blank_string response = get('204') assert_equal '', response.body end def adapter raise NotImplementedError.new("Need to override #adapter") end # extra options to pass when building the adapter def adapter_options [] end def create_connection(options = {}) if adapter == :default builder_block = nil else builder_block = Proc.new do |b| b.request :multipart b.request :url_encoded b.adapter adapter, *adapter_options end end server = self.class.live_server url = '%s://%s:%d' % [server.scheme, server.host, server.port] options[:ssl] ||= {} options[:ssl][:ca_file] ||= ENV['SSL_FILE'] Faraday::Connection.new(url, options, &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 end end end faraday-0.9.2/test/adapters/logger_test.rb000066400000000000000000000050511260477344700205750ustar00rootroot00000000000000require File.expand_path('../../helper', __FILE__) require 'stringio' require 'logger' module Adapters class LoggerTest < Faraday::TestCase def conn(logger, logger_options={}) rubbles = ['Barney', 'Betty', 'Bam Bam'] Faraday.new do |b| b.response :logger, logger, logger_options b.adapter :test do |stubs| stubs.get('/hello') { [200, {'Content-Type' => 'text/html'}, 'hello'] } stubs.post('/ohai') { [200, {'Content-Type' => 'text/html'}, 'fred'] } stubs.post('/ohyes') { [200, {'Content-Type' => 'text/html'}, 'pebbles'] } stubs.get('/rubbles') { [200, {'Content-Type' => 'application/json'}, rubbles] } end end end def setup @io = StringIO.new @logger = Logger.new(@io) @logger.level = Logger::DEBUG @conn = conn(@logger) @resp = @conn.get '/hello', nil, :accept => 'text/html' end def test_still_returns_output assert_equal 'hello', @resp.body end def test_logs_method_and_url assert_match "get http:/hello", @io.string end def test_logs_request_headers assert_match %(Accept: "text/html), @io.string end def test_logs_response_headers assert_match %(Content-Type: "text/html), @io.string end def test_does_not_log_request_body_by_default @conn.post '/ohai', 'name=Unagi', :accept => 'text/html' refute_match %(name=Unagi), @io.string end def test_does_not_log_response_body_by_default @conn.post '/ohai', 'name=Toro', :accept => 'text/html' refute_match %(fred), @io.string end def test_log_only_request_body app = conn(@logger, :bodies => { :request => true }) app.post '/ohyes', 'name=Tamago', :accept => 'text/html' assert_match %(name=Tamago), @io.string refute_match %(pebbles), @io.string end def test_log_only_response_body app = conn(@logger, :bodies => { :response => true }) app.post '/ohyes', 'name=Hamachi', :accept => 'text/html' assert_match %(pebbles), @io.string refute_match %(name=Hamachi), @io.string end def test_log_request_and_response_body app = conn(@logger, :bodies => true) app.post '/ohyes', 'name=Ebi', :accept => 'text/html' assert_match %(name=Ebi), @io.string assert_match %(pebbles), @io.string end def test_log_response_body_object app = conn(@logger, :bodies => true) app.get '/rubbles', nil, :accept => 'text/html' assert_match %([\"Barney\", \"Betty\", \"Bam Bam\"]\n), @io.string end end end faraday-0.9.2/test/adapters/net_http_persistent_test.rb000066400000000000000000000010311260477344700234150ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class NetHttpPersistentTest < Faraday::TestCase def adapter() :net_http_persistent end Integration.apply(self, :NonParallel) do def setup if defined?(Net::HTTP::Persistent) # work around problems with mixed SSL certificates # https://github.com/drbrain/net-http-persistent/issues/45 http = Net::HTTP::Persistent.new('Faraday') http.ssl_cleanup(4) end end if ssl_mode? end end end faraday-0.9.2/test/adapters/net_http_test.rb000066400000000000000000000020671260477344700211470ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) require 'ostruct' require 'uri' module Adapters class NetHttpTest < Faraday::TestCase def adapter() :net_http end behaviors = [:NonParallel] behaviors << :Compression if RUBY_VERSION >= '1.9' Integration.apply(self, *behaviors) def test_no_explicit_http_port_number url = URI('http://example.com') url.port = nil adapter = Faraday::Adapter::NetHttp.new http = adapter.net_http_connection(:url => url, :request => {}) assert_equal 80, http.port end def test_no_explicit_https_port_number url = URI('https://example.com') url.port = nil adapter = Faraday::Adapter::NetHttp.new http = adapter.net_http_connection(:url => url, :request => {}) assert_equal 443, http.port end def test_explicit_port_number url = URI('https://example.com:1234') adapter = Faraday::Adapter::NetHttp.new http = adapter.net_http_connection(:url => url, :request => {}) assert_equal 1234, http.port end end end faraday-0.9.2/test/adapters/patron_test.rb000066400000000000000000000010171260477344700206170ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class Patron < Faraday::TestCase def adapter() :patron end Integration.apply(self, :NonParallel) do # https://github.com/toland/patron/issues/34 undef :test_PATCH_send_url_encoded_params # https://github.com/toland/patron/issues/52 undef :test_GET_with_body # no support for SSL peer verification undef :test_GET_ssl_fails_with_bad_cert if ssl_mode? end unless RUBY_VERSION < '1.9' or jruby? end end faraday-0.9.2/test/adapters/rack_test.rb000066400000000000000000000014331260477344700202360ustar00rootroot00000000000000require File.expand_path("../integration", __FILE__) require File.expand_path('../../live_server', __FILE__) module Adapters class RackTest < Faraday::TestCase def adapter() :rack end def adapter_options [Faraday::LiveServer] end # no Integration.apply because this doesn't require a server as a separate process include Integration::Common include Integration::NonParallel # not using shared test because error is swallowed by Sinatra def test_timeout conn = create_connection(:request => {:timeout => 1, :open_timeout => 1}) begin conn.get '/slow' rescue Faraday::Error::ClientError end end # test not applicable undef test_connection_error undef test_proxy undef test_proxy_auth_fail end end faraday-0.9.2/test/adapters/test_middleware_test.rb000066400000000000000000000072121260477344700224730ustar00rootroot00000000000000require File.expand_path('../../helper', __FILE__) module Adapters class TestMiddleware < Faraday::TestCase Stubs = Faraday::Adapter.lookup_middleware(:test)::Stubs def setup @stubs = Stubs.new @conn = Faraday.new do |builder| builder.adapter :test, @stubs end @stubs.get('/hello') { [200, {'Content-Type' => 'text/html'}, 'hello'] } @resp = @conn.get('/hello') end def test_middleware_with_simple_path_sets_status assert_equal 200, @resp.status end def test_middleware_with_simple_path_sets_headers assert_equal 'text/html', @resp.headers['Content-Type'] end def test_middleware_with_simple_path_sets_body assert_equal 'hello', @resp.body end def test_middleware_can_be_called_several_times assert_equal 'hello', @conn.get("/hello").body end def test_middleware_with_get_params @stubs.get('/param?a=1') { [200, {}, 'a'] } assert_equal 'a', @conn.get('/param?a=1').body end def test_middleware_ignores_unspecified_get_params @stubs.get('/optional?a=1') { [200, {}, 'a'] } assert_equal 'a', @conn.get('/optional?a=1&b=1').body assert_equal 'a', @conn.get('/optional?a=1').body assert_raises Faraday::Adapter::Test::Stubs::NotFound do @conn.get('/optional') end end def test_middleware_with_http_headers @stubs.get('/yo', { 'X-HELLO' => 'hello' }) { [200, {}, 'a'] } @stubs.get('/yo') { [200, {}, 'b'] } assert_equal 'a', @conn.get('/yo') { |env| env.headers['X-HELLO'] = 'hello' }.body assert_equal 'b', @conn.get('/yo').body end def test_middleware_allow_different_outcomes_for_the_same_request @stubs.get('/hello') { [200, {'Content-Type' => 'text/html'}, 'hello'] } @stubs.get('/hello') { [200, {'Content-Type' => 'text/html'}, 'world'] } assert_equal 'hello', @conn.get("/hello").body assert_equal 'world', @conn.get("/hello").body end def test_yields_env_to_stubs @stubs.get '/hello' do |env| assert_equal '/hello', env[:url].path assert_equal 'foo.com', env[:url].host assert_equal '1', env[:params]['a'] assert_equal 'text/plain', env[:request_headers]['Accept'] [200, {}, 'a'] end @conn.headers['Accept'] = 'text/plain' assert_equal 'a', @conn.get('http://foo.com/hello?a=1').body end def test_parses_params_with_default_encoder @stubs.get '/hello' do |env| assert_equal '1', env[:params]['a']['b'] [200, {}, 'a'] end assert_equal 'a', @conn.get('http://foo.com/hello?a[b]=1').body end def test_parses_params_with_nested_encoder @stubs.get '/hello' do |env| assert_equal '1', env[:params]['a']['b'] [200, {}, 'a'] end @conn.options.params_encoder = Faraday::NestedParamsEncoder assert_equal 'a', @conn.get('http://foo.com/hello?a[b]=1').body end def test_parses_params_with_flat_encoder @stubs.get '/hello' do |env| assert_equal '1', env[:params]['a[b]'] [200, {}, 'a'] end @conn.options.params_encoder = Faraday::FlatParamsEncoder assert_equal 'a', @conn.get('http://foo.com/hello?a[b]=1').body end def test_raises_an_error_if_no_stub_is_found_for_request assert_raises Stubs::NotFound do @conn.get('/invalid'){ [200, {}, []] } end end def test_raises_an_error_if_no_stub_is_found_for_request_without_this_header @stubs.get('/yo', { 'X-HELLO' => 'hello' }) { [200, {}, 'a'] } assert_raises Faraday::Adapter::Test::Stubs::NotFound do @conn.get('/yo') end end end end faraday-0.9.2/test/adapters/typhoeus_test.rb000066400000000000000000000015051260477344700211760ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class TyphoeusTest < Faraday::TestCase def adapter() :typhoeus end Integration.apply(self, :Parallel) do # https://github.com/dbalatero/typhoeus/issues/75 undef :test_GET_with_body # Not a Typhoeus bug, but WEBrick inability to handle "100-continue" # which libcurl seems to generate for this particular request: undef :test_POST_sends_files # inconsistent outcomes ranging from successful response to connection error undef :test_proxy_auth_fail if ssl_mode? def test_binds_local_socket host = '1.2.3.4' conn = create_connection :request => { :bind => { :host => host } } assert_equal host, conn.options[:bind][:host] end end unless jruby? or ruby_22_plus? end end faraday-0.9.2/test/authentication_middleware_test.rb000066400000000000000000000043471260477344700227360ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class AuthenticationMiddlewareTest < Faraday::TestCase def conn Faraday::Connection.new('http://example.net/') do |builder| yield(builder) builder.adapter :test do |stub| stub.get('/auth-echo') do |env| [200, {}, env[:request_headers]['Authorization']] end end end end def test_basic_middleware_adds_basic_header response = conn { |b| b.request :basic_auth, 'aladdin', 'opensesame' }.get('/auth-echo') assert_equal 'Basic YWxhZGRpbjpvcGVuc2VzYW1l', response.body end def test_basic_middleware_adds_basic_header_correctly_with_long_values response = conn { |b| b.request :basic_auth, 'A' * 255, '' }.get('/auth-echo') assert_equal "Basic #{'QUFB' * 85}Og==", response.body end def test_basic_middleware_does_not_interfere_with_existing_authorization response = conn { |b| b.request :basic_auth, 'aladdin', 'opensesame' }. get('/auth-echo', nil, :authorization => 'Token token="bar"') assert_equal 'Token token="bar"', response.body end def test_token_middleware_adds_token_header response = conn { |b| b.request :token_auth, 'quux' }.get('/auth-echo') assert_equal 'Token token="quux"', response.body end def test_token_middleware_includes_other_values_if_provided response = conn { |b| b.request :token_auth, 'baz', :foo => 42 }.get('/auth-echo') assert_match(/^Token /, response.body) assert_match(/token="baz"/, response.body) assert_match(/foo="42"/, response.body) end def test_token_middleware_does_not_interfere_with_existing_authorization response = conn { |b| b.request :token_auth, 'quux' }. get('/auth-echo', nil, :authorization => 'Token token="bar"') assert_equal 'Token token="bar"', response.body end def test_authorization_middleware_with_string response = conn { |b| b.request :authorization, 'custom', 'abc def' }.get('/auth-echo') assert_match(/^custom abc def$/, response.body) end def test_authorization_middleware_with_hash response = conn { |b| b.request :authorization, 'baz', :foo => 42 }.get('/auth-echo') assert_match(/^baz /, response.body) assert_match(/foo="42"/, response.body) end end faraday-0.9.2/test/composite_read_io_test.rb000066400000000000000000000060121260477344700211750ustar00rootroot00000000000000require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) require 'stringio' class CompositeReadIOTest < Faraday::TestCase Part = Struct.new(:to_io) do def length() to_io.string.length end end def part(str) Part.new StringIO.new(str) end def composite_io(*parts) Faraday::CompositeReadIO.new(*parts) end def test_empty io = composite_io assert_equal 0, io.length assert_equal "", io.read end def test_empty_returns_nil_for_limited_read assert_nil composite_io.read(1) end def test_empty_parts_returns_nil_for_limited_read io = composite_io(part(""), part("")) assert_nil io.read(1) end def test_multipart_read_all io = composite_io(part("abcd"), part("1234")) assert_equal 8, io.length assert_equal "abcd1234", io.read end def test_multipart_read_limited io = composite_io(part("abcd"), part("1234")) assert_equal "abc", io.read(3) assert_equal "d12", io.read(3) assert_equal "34", io.read(3) assert_equal nil, io.read(3) assert_equal nil, io.read(3) end def test_multipart_read_limited_size_larger_than_part io = composite_io(part("abcd"), part("1234")) assert_equal "abcd12", io.read(6) assert_equal "34", io.read(6) assert_equal nil, io.read(6) end def test_multipart_read_with_blank_parts io = composite_io(part(""), part("abcd"), part(""), part("1234"), part("")) assert_equal "abcd12", io.read(6) assert_equal "34", io.read(6) assert_equal nil, io.read(6) end def test_multipart_rewind io = composite_io(part("abcd"), part("1234")) assert_equal "abc", io.read(3) assert_equal "d12", io.read(3) io.rewind assert_equal "abc", io.read(3) assert_equal "d1234", io.read(5) assert_equal nil, io.read(3) io.rewind assert_equal "ab", io.read(2) end # JRuby enforces types to copy_stream to be String or IO if IO.respond_to?(:copy_stream) && !jruby? def test_compatible_with_copy_stream target_io = StringIO.new def target_io.ensure_open_and_writable # Rubinius compatibility end io = composite_io(part("abcd"), part("1234")) Faraday::Timer.timeout(1) do IO.copy_stream(io, target_io) end assert_equal "abcd1234", target_io.string end end unless RUBY_VERSION < '1.9' def test_read_from_multibyte File.open(File.dirname(__FILE__) + '/multibyte.txt') do |utf8| io = composite_io(part("\x86"), Part.new(utf8)) assert_equal bin("\x86\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\n"), io.read end end def test_limited_from_multibyte File.open(File.dirname(__FILE__) + '/multibyte.txt') do |utf8| io = composite_io(part("\x86"), Part.new(utf8)) assert_equal bin("\x86\xE3\x83"), io.read(3) assert_equal bin("\x95\xE3\x82"), io.read(3) assert_equal bin("\xA1\xE3\x82\xA4\xE3\x83\xAB\n"), io.read(8) end end end def bin(str) str.force_encoding("BINARY") if str.respond_to?(:force_encoding) str end end faraday-0.9.2/test/connection_test.rb000066400000000000000000000416731260477344700176640ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class TestConnection < Faraday::TestCase def with_env(key, proxy) old_value = ENV.fetch(key, false) ENV[key] = proxy begin yield ensure if old_value == false ENV.delete key else ENV[key] = old_value end end end def test_initialize_parses_host_out_of_given_url conn = Faraday::Connection.new "http://sushi.com" assert_equal 'sushi.com', conn.host end def test_initialize_inherits_default_port_out_of_given_url conn = Faraday::Connection.new "http://sushi.com" assert_equal 80, conn.port end def test_initialize_parses_scheme_out_of_given_url conn = Faraday::Connection.new "http://sushi.com" assert_equal 'http', conn.scheme end def test_initialize_parses_port_out_of_given_url conn = Faraday::Connection.new "http://sushi.com:815" assert_equal 815, conn.port end def test_initialize_parses_nil_path_prefix_out_of_given_url conn = Faraday::Connection.new "http://sushi.com" assert_equal '/', conn.path_prefix end def test_initialize_parses_path_prefix_out_of_given_url conn = Faraday::Connection.new "http://sushi.com/fish" assert_equal '/fish', conn.path_prefix end def test_initialize_parses_path_prefix_out_of_given_url_option conn = Faraday::Connection.new :url => "http://sushi.com/fish" assert_equal '/fish', conn.path_prefix end def test_initialize_stores_default_params_from_options conn = Faraday::Connection.new :params => {:a => 1} assert_equal({'a' => 1}, conn.params) end def test_initialize_stores_default_params_from_uri conn = Faraday::Connection.new "http://sushi.com/fish?a=1" assert_equal({'a' => '1'}, conn.params) end def test_initialize_stores_default_params_from_uri_and_options conn = Faraday::Connection.new "http://sushi.com/fish?a=1&b=2", :params => {'a' => 3} assert_equal({'a' => 3, 'b' => '2'}, conn.params) end def test_initialize_stores_default_headers_from_options conn = Faraday::Connection.new :headers => {:user_agent => 'Faraday'} assert_equal 'Faraday', conn.headers['User-agent'] end def test_basic_auth_sets_header conn = Faraday::Connection.new assert_nil conn.headers['Authorization'] conn.basic_auth 'Aladdin', 'open sesame' assert auth = conn.headers['Authorization'] assert_equal 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', auth end def test_auto_parses_basic_auth_from_url_and_unescapes conn = Faraday::Connection.new :url => "http://foo%40bar.com:pass%20word@sushi.com/fish" assert auth = conn.headers['Authorization'] assert_equal Faraday::Request::BasicAuthentication.header("foo@bar.com", "pass word"), auth end def test_token_auth_sets_header conn = Faraday::Connection.new assert_nil conn.headers['Authorization'] conn.token_auth 'abcdef', :nonce => 'abc' assert auth = conn.headers['Authorization'] assert_match(/^Token /, auth) assert_match(/token="abcdef"/, auth) assert_match(/nonce="abc"/, auth) end def test_build_exclusive_url_uses_connection_host_as_default_uri_host conn = Faraday::Connection.new conn.host = 'sushi.com' uri = conn.build_exclusive_url("/sake.html") assert_equal 'sushi.com', uri.host end def test_build_exclusive_url_overrides_connection_port_for_absolute_urls conn = Faraday::Connection.new conn.port = 23 uri = conn.build_exclusive_url("http://sushi.com") assert_equal 80, uri.port end def test_build_exclusive_url_uses_connection_scheme_as_default_uri_scheme conn = Faraday::Connection.new 'http://sushi.com' uri = conn.build_exclusive_url("/sake.html") assert_equal 'http', uri.scheme end def test_build_exclusive_url_uses_connection_path_prefix_to_customize_path conn = Faraday::Connection.new conn.path_prefix = '/fish' uri = conn.build_exclusive_url("sake.html") assert_equal '/fish/sake.html', uri.path end def test_build_exclusive_url_uses_root_connection_path_prefix_to_customize_path conn = Faraday::Connection.new conn.path_prefix = '/' uri = conn.build_exclusive_url("sake.html") assert_equal '/sake.html', uri.path end def test_build_exclusive_url_forces_connection_path_prefix_to_be_absolute conn = Faraday::Connection.new conn.path_prefix = 'fish' uri = conn.build_exclusive_url("sake.html") assert_equal '/fish/sake.html', uri.path end def test_build_exclusive_url_ignores_connection_path_prefix_trailing_slash conn = Faraday::Connection.new conn.path_prefix = '/fish/' uri = conn.build_exclusive_url("sake.html") assert_equal '/fish/sake.html', uri.path end def test_build_exclusive_url_allows_absolute_uri_to_ignore_connection_path_prefix conn = Faraday::Connection.new conn.path_prefix = '/fish' uri = conn.build_exclusive_url("/sake.html") assert_equal '/sake.html', uri.path end def test_build_exclusive_url_parses_url_params_into_path conn = Faraday::Connection.new uri = conn.build_exclusive_url("http://sushi.com/sake.html") assert_equal '/sake.html', uri.path end def test_build_exclusive_url_doesnt_add_ending_slash_given_nil_url conn = Faraday::Connection.new conn.url_prefix = "http://sushi.com/nigiri" uri = conn.build_exclusive_url assert_equal "/nigiri", uri.path end def test_build_exclusive_url_doesnt_add_ending_slash_given_empty_url conn = Faraday::Connection.new conn.url_prefix = "http://sushi.com/nigiri" uri = conn.build_exclusive_url('') assert_equal "/nigiri", uri.path end def test_build_exclusive_url_doesnt_use_connection_params conn = Faraday::Connection.new "http://sushi.com/nigiri" conn.params = {:a => 1} assert_equal "http://sushi.com/nigiri", conn.build_exclusive_url.to_s end def test_build_exclusive_url_uses_argument_params conn = Faraday::Connection.new "http://sushi.com/nigiri" conn.params = {:a => 1} params = Faraday::Utils::ParamsHash.new params[:a] = 2 url = conn.build_exclusive_url(nil, params) assert_equal "http://sushi.com/nigiri?a=2", url.to_s end def test_build_url_uses_params conn = Faraday::Connection.new "http://sushi.com/nigiri" conn.params = {:a => 1, :b => 1} assert_equal "http://sushi.com/nigiri?a=1&b=1", conn.build_url.to_s end def test_build_url_merges_params conn = Faraday::Connection.new "http://sushi.com/nigiri" conn.params = {:a => 1, :b => 1} url = conn.build_url(nil, :b => 2, :c => 3) assert_equal "http://sushi.com/nigiri?a=1&b=2&c=3", url.to_s end def test_request_header_change_does_not_modify_connection_header connection = Faraday.new(:url => "https://asushi.com/sake.html") connection.headers = { "Authorization"=>"token abc123" } request = connection.build_request(:get) request.headers.delete("Authorization") assert_equal connection.headers.keys.sort, ["Authorization"] assert connection.headers.include?("Authorization") assert_equal request.headers.keys.sort, [] assert !request.headers.include?("Authorization") end def test_env_url_parses_url_params_into_query uri = env_url("http://sushi.com/sake.html", 'a[b]' => '1 + 2') assert_equal "a%5Bb%5D=1+%2B+2", uri.query end def test_env_url_escapes_per_spec uri = env_url('http:/', 'a' => '1+2 foo~bar.-baz') assert_equal "a=1%2B2+foo~bar.-baz", uri.query end def test_env_url_bracketizes_nested_params_in_query url = env_url nil, 'a' => {'b' => 'c'} assert_equal "a%5Bb%5D=c", url.query end def test_env_url_bracketizes_repeated_params_in_query uri = env_url("http://sushi.com/sake.html", 'a' => [1, 2]) assert_equal "a%5B%5D=1&a%5B%5D=2", uri.query end def test_env_url_without_braketizing_repeated_params_in_query uri = env_url 'http://sushi.com', 'a' => [1, 2] do |conn| conn.options.params_encoder = Faraday::FlatParamsEncoder end assert_equal "a=1&a=2", uri.query end def test_build_exclusive_url_parses_url conn = Faraday::Connection.new uri = conn.build_exclusive_url("http://sushi.com/sake.html") assert_equal "http", uri.scheme assert_equal "sushi.com", uri.host assert_equal '/sake.html', uri.path end def test_build_exclusive_url_parses_url_and_changes_scheme conn = Faraday::Connection.new :url => "http://sushi.com/sushi" conn.scheme = 'https' uri = conn.build_exclusive_url("sake.html") assert_equal 'https://sushi.com/sushi/sake.html', uri.to_s end def test_build_exclusive_url_joins_url_to_base_with_ending_slash conn = Faraday::Connection.new :url => "http://sushi.com/sushi/" uri = conn.build_exclusive_url("sake.html") assert_equal 'http://sushi.com/sushi/sake.html', uri.to_s end def test_build_exclusive_url_used_default_base_with_ending_slash conn = Faraday::Connection.new :url => "http://sushi.com/sushi/" uri = conn.build_exclusive_url assert_equal 'http://sushi.com/sushi/', uri.to_s end def test_build_exclusive_url_overrides_base conn = Faraday::Connection.new :url => "http://sushi.com/sushi/" uri = conn.build_exclusive_url('/sake/') assert_equal 'http://sushi.com/sake/', uri.to_s end def test_build_exclusive_url_handles_uri_instances conn = Faraday::Connection.new uri = conn.build_exclusive_url(URI('/sake.html')) assert_equal '/sake.html', uri.path end def test_proxy_accepts_string with_env 'http_proxy', "http://duncan.proxy.com:80" do conn = Faraday::Connection.new conn.proxy 'http://proxy.com' assert_equal 'proxy.com', conn.proxy.host end end def test_proxy_accepts_uri with_env 'http_proxy', "http://duncan.proxy.com:80" do conn = Faraday::Connection.new conn.proxy URI.parse('http://proxy.com') assert_equal 'proxy.com', conn.proxy.host end end def test_proxy_accepts_hash_with_string_uri with_env 'http_proxy', "http://duncan.proxy.com:80" do conn = Faraday::Connection.new conn.proxy :uri => 'http://proxy.com', :user => 'rick' assert_equal 'proxy.com', conn.proxy.host assert_equal 'rick', conn.proxy.user end end def test_proxy_accepts_hash with_env 'http_proxy', "http://duncan.proxy.com:80" do conn = Faraday::Connection.new conn.proxy :uri => URI.parse('http://proxy.com'), :user => 'rick' assert_equal 'proxy.com', conn.proxy.host assert_equal 'rick', conn.proxy.user end end def test_proxy_accepts_http_env with_env 'http_proxy', "http://duncan.proxy.com:80" do conn = Faraday::Connection.new assert_equal 'duncan.proxy.com', conn.proxy.host end end def test_proxy_accepts_http_env_with_auth with_env 'http_proxy', "http://a%40b:my%20pass@duncan.proxy.com:80" do conn = Faraday::Connection.new assert_equal 'a@b', conn.proxy.user assert_equal 'my pass', conn.proxy.password end end def test_proxy_accepts_env_without_scheme with_env 'http_proxy', "localhost:8888" do uri = Faraday::Connection.new.proxy[:uri] assert_equal 'localhost', uri.host assert_equal 8888, uri.port end end def test_no_proxy_from_env with_env 'http_proxy', nil do conn = Faraday::Connection.new assert_equal nil, conn.proxy end end def test_no_proxy_from_blank_env with_env 'http_proxy', '' do conn = Faraday::Connection.new assert_equal nil, conn.proxy end end def test_proxy_doesnt_accept_uppercase_env with_env 'HTTP_PROXY', "http://localhost:8888/" do conn = Faraday::Connection.new assert_nil conn.proxy end end def test_proxy_requires_uri conn = Faraday::Connection.new assert_raises ArgumentError do conn.proxy :uri => :bad_uri, :user => 'rick' end end def test_dups_connection_object conn = Faraday::Connection.new 'http://sushi.com/foo', :ssl => { :verify => :none }, :headers => {'content-type' => 'text/plain'}, :params => {'a'=>'1'}, :request => {:timeout => 5} other = conn.dup assert_equal conn.build_exclusive_url, other.build_exclusive_url assert_equal 'text/plain', other.headers['content-type'] assert_equal '1', other.params['a'] other.basic_auth('', '') other.headers['content-length'] = 12 other.params['b'] = '2' other.options[:open_timeout] = 10 assert_equal 2, other.builder.handlers.size assert_equal 2, conn.builder.handlers.size assert !conn.headers.key?('content-length') assert !conn.params.key?('b') assert_equal 5, other.options[:timeout] assert_nil conn.options[:open_timeout] end def test_initialize_with_false_option conn = Faraday::Connection.new :ssl => {:verify => false} assert !conn.ssl.verify? end def test_init_with_block conn = Faraday::Connection.new { } assert_equal 0, conn.builder.handlers.size end def test_init_with_block_yields_connection conn = Faraday::Connection.new(:params => {'a'=>'1'}) { |faraday| faraday.adapter :net_http faraday.url_prefix = 'http://sushi.com/omnom' assert_equal '1', faraday.params['a'] } assert_equal 1, conn.builder.handlers.size assert_equal '/omnom', conn.path_prefix end def env_url(url, params) conn = Faraday::Connection.new(url, :params => params) yield(conn) if block_given? req = conn.build_request(:get) req.to_env(conn).url end end class TestRequestParams < Faraday::TestCase def create_connection(*args) @conn = Faraday::Connection.new(*args) do |conn| yield(conn) if block_given? class << conn.builder undef app def app() lambda { |env| env } end end end end def assert_query_equal(expected, query) assert_equal expected, query.split('&').sort end def with_default_params_encoder(encoder) old_encoder = Faraday::Utils.default_params_encoder begin Faraday::Utils.default_params_encoder = encoder yield ensure Faraday::Utils.default_params_encoder = old_encoder end end def test_merges_connection_and_request_params create_connection 'http://a.co/?token=abc', :params => {'format' => 'json'} query = get '?page=1', :limit => 5 assert_query_equal %w[format=json limit=5 page=1 token=abc], query end def test_overrides_connection_params create_connection 'http://a.co/?a=a&b=b&c=c', :params => {:a => 'A'} do |conn| conn.params[:b] = 'B' assert_equal 'c', conn.params[:c] end assert_query_equal %w[a=A b=B c=c], get end def test_all_overrides_connection_params create_connection 'http://a.co/?a=a', :params => {:c => 'c'} do |conn| conn.params = {'b' => 'b'} end assert_query_equal %w[b=b], get end def test_overrides_request_params create_connection query = get '?p=1&a=a', :p => 2 assert_query_equal %w[a=a p=2], query end def test_overrides_request_params_block create_connection query = get '?p=1&a=a', :p => 2 do |req| req.params[:p] = 3 end assert_query_equal %w[a=a p=3], query end def test_overrides_request_params_block_url create_connection query = get nil, :p => 2 do |req| req.url '?p=1&a=a', 'p' => 3 end assert_query_equal %w[a=a p=3], query end def test_overrides_all_request_params create_connection :params => {:c => 'c'} query = get '?p=1&a=a', :p => 2 do |req| assert_equal 'a', req.params[:a] assert_equal 'c', req.params['c'] assert_equal 2, req.params['p'] req.params = {:b => 'b'} assert_equal 'b', req.params['b'] end assert_query_equal %w[b=b], query end def test_array_params_in_url with_default_params_encoder(nil) do create_connection 'http://a.co/page1?color[]=red&color[]=blue' query = get assert_equal "color%5B%5D=red&color%5B%5D=blue", query end end def test_array_params_in_params with_default_params_encoder(nil) do create_connection 'http://a.co/page1', :params => {:color => ['red', 'blue']} query = get assert_equal "color%5B%5D=red&color%5B%5D=blue", query end end def test_array_params_in_url_with_flat_params with_default_params_encoder(Faraday::FlatParamsEncoder) do create_connection 'http://a.co/page1?color=red&color=blue' query = get assert_equal "color=red&color=blue", query end end def test_array_params_in_params_with_flat_params with_default_params_encoder(Faraday::FlatParamsEncoder) do create_connection 'http://a.co/page1', :params => {:color => ['red', 'blue']} query = get assert_equal "color=red&color=blue", query end end def test_params_with_connection_options encoder = Object.new def encoder.encode(params) params.map { |k,v| "#{k.upcase}-#{v.upcase}" }.join(',') end create_connection :params => {:color => 'red'} query = get('', :feeling => 'blue') do |req| req.options.params_encoder = encoder end assert_equal ["COLOR-RED", "FEELING-BLUE"], query.split(",").sort end def get(*args) env = @conn.get(*args) do |req| yield(req) if block_given? end env[:url].query end end faraday-0.9.2/test/env_test.rb000066400000000000000000000163031260477344700163050ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class EnvTest < Faraday::TestCase def setup @conn = Faraday.new :url => 'http://sushi.com/api', :headers => {'Mime-Version' => '1.0'}, :request => {:oauth => {:consumer_key => 'anonymous'}} @conn.options.timeout = 3 @conn.options.open_timeout = 5 @conn.ssl.verify = false @conn.proxy 'http://proxy.com' end def test_request_create_stores_method env = make_env(:get) assert_equal :get, env.method end def test_request_create_stores_uri env = make_env do |req| req.url 'foo.json', 'a' => 1 end assert_equal 'http://sushi.com/api/foo.json?a=1', env.url.to_s end def test_request_create_stores_headers env = make_env do |req| req['Server'] = 'Faraday' end headers = env.request_headers assert_equal '1.0', headers['mime-version'] assert_equal 'Faraday', headers['server'] end def test_request_create_stores_body env = make_env do |req| req.body = 'hi' end assert_equal 'hi', env.body end def test_global_request_options env = make_env assert_equal 3, env.request.timeout assert_equal 5, env.request.open_timeout end def test_per_request_options env = make_env do |req| req.options.timeout = 10 req.options.boundary = 'boo' req.options.oauth[:consumer_secret] = 'xyz' end assert_equal 10, env.request.timeout assert_equal 5, env.request.open_timeout assert_equal 'boo', env.request.boundary oauth_expected = {:consumer_secret => 'xyz', :consumer_key => 'anonymous'} assert_equal oauth_expected, env.request.oauth end def test_request_create_stores_ssl_options env = make_env assert_equal false, env.ssl.verify end def test_request_create_stores_proxy_options env = make_env assert_equal 'proxy.com', env.request.proxy.host end def test_custom_members_are_retained env = make_env env[:foo] = "custom 1" env[:bar] = :custom_2 env2 = Faraday::Env.from(env) assert_equal "custom 1", env2[:foo] assert_equal :custom_2, env2[:bar] env2[:baz] = "custom 3" assert_nil env[:baz] end private def make_env(method = :get, connection = @conn, &block) request = connection.build_request(method, &block) request.to_env(connection) end end class HeadersTest < Faraday::TestCase def setup @headers = Faraday::Utils::Headers.new end def test_normalizes_different_capitalizations @headers['Content-Type'] = 'application/json' assert_equal ['Content-Type'], @headers.keys assert_equal 'application/json', @headers['Content-Type'] assert_equal 'application/json', @headers['CONTENT-TYPE'] assert_equal 'application/json', @headers['content-type'] assert @headers.include?('content-type') @headers['content-type'] = 'application/xml' assert_equal ['Content-Type'], @headers.keys assert_equal 'application/xml', @headers['Content-Type'] assert_equal 'application/xml', @headers['CONTENT-TYPE'] assert_equal 'application/xml', @headers['content-type'] end def test_fetch_key @headers['Content-Type'] = 'application/json' block_called = false assert_equal 'application/json', @headers.fetch('content-type') { block_called = true } assert_equal 'application/json', @headers.fetch('Content-Type') assert_equal 'application/json', @headers.fetch('CONTENT-TYPE') assert_equal 'application/json', @headers.fetch(:content_type) assert_equal false, block_called assert_equal 'default', @headers.fetch('invalid', 'default') assert_equal false, @headers.fetch('invalid', false) assert_nil @headers.fetch('invalid', nil) assert_equal 'Invalid key', @headers.fetch('Invalid') { |key| "#{key} key" } expected_error = defined?(KeyError) ? KeyError : IndexError assert_raises(expected_error) { @headers.fetch('invalid') } end def test_delete_key @headers['Content-Type'] = 'application/json' assert_equal 1, @headers.size assert @headers.include?('content-type') assert_equal 'application/json', @headers.delete('content-type') assert_equal 0, @headers.size assert !@headers.include?('content-type') assert_equal nil, @headers.delete('content-type') end def test_parse_response_headers_leaves_http_status_line_out @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n") assert_equal %w(Content-Type), @headers.keys end def test_parse_response_headers_parses_lower_cased_header_name_and_value @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n") assert_equal 'text/html', @headers['content-type'] end def test_parse_response_headers_parses_lower_cased_header_name_and_value_with_colon @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://sushi.com/\r\n\r\n") assert_equal 'http://sushi.com/', @headers['location'] end def test_parse_response_headers_parses_blank_lines @headers.parse("HTTP/1.1 200 OK\r\n\r\nContent-Type: text/html\r\n\r\n") assert_equal 'text/html', @headers['content-type'] end end class ResponseTest < Faraday::TestCase def setup @env = Faraday::Env.from \ :status => 404, :body => 'yikes', :response_headers => {'Content-Type' => 'text/plain'} @response = Faraday::Response.new @env end def test_finished assert @response.finished? end def test_error_on_finish assert_raises RuntimeError do @response.finish({}) end end def test_body_is_parsed_on_finish response = Faraday::Response.new response.on_complete { |env| env[:body] = env[:body].upcase } response.finish(@env) assert_equal "YIKES", response.body end def test_response_body_is_available_during_on_complete response = Faraday::Response.new response.on_complete { |env| env[:body] = response.body.upcase } response.finish(@env) assert_equal "YIKES", response.body end def test_env_in_on_complete_is_identical_to_response_env response = Faraday::Response.new callback_env = nil response.on_complete { |env| callback_env = env } response.finish({}) assert_same response.env, callback_env end def test_not_success assert !@response.success? end def test_status assert_equal 404, @response.status end def test_body assert_equal 'yikes', @response.body end def test_headers assert_equal 'text/plain', @response.headers['Content-Type'] assert_equal 'text/plain', @response['content-type'] end def test_apply_request @response.apply_request :body => 'a=b', :method => :post assert_equal 'yikes', @response.body assert_equal :post, @response.env[:method] end def test_marshal @response = Faraday::Response.new @response.on_complete { } @response.finish @env.merge(:params => 'moo') loaded = Marshal.load Marshal.dump(@response) assert_nil loaded.env[:params] assert_equal %w[body response_headers status], loaded.env.keys.map { |k| k.to_s }.sort end def test_hash hash = @response.to_hash assert_kind_of Hash, hash assert_equal @env.to_hash, hash assert_equal hash[:status], @response.status assert_equal hash[:response_headers], @response.headers assert_equal hash[:body], @response.body end end faraday-0.9.2/test/helper.rb000066400000000000000000000026721260477344700157410ustar00rootroot00000000000000if RUBY_VERSION >= '1.9' require 'simplecov' require 'coveralls' SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] SimpleCov.start do add_filter '/bundle/' add_filter '/test/' minimum_coverage(87) end end gem 'minitest' if defined? Bundler require 'minitest/autorun' require File.expand_path('../../lib/faraday', __FILE__) require 'stringio' require 'uri' module Faraday module LiveServerConfig def live_server=(value) @@live_server = case value when /^http/ URI(value) when /./ URI('http://127.0.0.1:4567') end end def live_server? defined? @@live_server end # Returns an object that responds to `host` and `port`. def live_server live_server? and @@live_server end end class TestCase < MiniTest::Test extend LiveServerConfig self.live_server = ENV['LIVE'] def test_default assert true end unless defined? ::MiniTest def capture_warnings old, $stderr = $stderr, StringIO.new begin yield $stderr.string ensure $stderr = old end end def self.jruby? defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE end def self.rbx? defined? RUBY_ENGINE and 'rbx' == RUBY_ENGINE end def self.ruby_22_plus? RUBY_VERSION > '2.2' end def self.ssl_mode? ENV['SSL'] == 'yes' end end end faraday-0.9.2/test/live_server.rb000066400000000000000000000024401260477344700170000ustar00rootroot00000000000000require 'sinatra/base' module Faraday class LiveServer < Sinatra::Base set :environment, :test disable :logging disable :protection [:get, :post, :put, :patch, :delete, :options].each do |method| send(method, '/echo') do kind = request.request_method.downcase out = kind.dup out << ' ?' << request.GET.inspect if request.GET.any? out << ' ' << request.POST.inspect if request.POST.any? content_type 'text/plain' return out end end get '/echo_header' do header = "HTTP_#{params[:name].tr('-', '_').upcase}" request.env.fetch(header) { 'NONE' } end post '/file' do if params[:uploaded_file].respond_to? :each_key "file %s %s %d" % [ params[:uploaded_file][:filename], params[:uploaded_file][:type], params[:uploaded_file][:tempfile].size ] else status 400 end end get '/multi' do [200, { 'Set-Cookie' => 'one, two' }, ''] end get '/who-am-i' do request.env['REMOTE_ADDR'] end get '/slow' do sleep 10 [200, {}, 'ok'] end get '/204' do status 204 # no content end get '/ssl' do request.secure?.to_s end error do |e| "#{e.class}\n#{e.to_s}\n#{e.backtrace.join("\n")}" end end end if $0 == __FILE__ Faraday::LiveServer.run! end faraday-0.9.2/test/middleware/000077500000000000000000000000001260477344700162435ustar00rootroot00000000000000faraday-0.9.2/test/middleware/instrumentation_test.rb000066400000000000000000000041131260477344700230710ustar00rootroot00000000000000require File.expand_path("../../helper", __FILE__) module Middleware class InstrumentationTest < Faraday::TestCase def setup @instrumenter = FakeInstrumenter.new end def test_default_name assert_equal 'request.faraday', options.name end def test_default_instrumenter begin instrumenter = options.instrumenter rescue NameError => err assert_match 'ActiveSupport', err.to_s else assert_equal ActiveSupport::Notifications, instrumenter end end def test_name assert_equal 'booya', options(:name => 'booya').name end def test_instrumenter assert_equal :boom, options(:instrumenter => :boom).instrumenter end def test_instrumentation_with_default_name assert_equal 0, @instrumenter.instrumentations.size faraday = conn res = faraday.get '/' assert_equal 'ok', res.body assert_equal 1, @instrumenter.instrumentations.size name, env = @instrumenter.instrumentations.first assert_equal 'request.faraday', name assert_equal '/', env[:url].path end def test_instrumentation assert_equal 0, @instrumenter.instrumentations.size faraday = conn :name => 'booya' res = faraday.get '/' assert_equal 'ok', res.body assert_equal 1, @instrumenter.instrumentations.size name, env = @instrumenter.instrumentations.first assert_equal 'booya', name assert_equal '/', env[:url].path end class FakeInstrumenter attr_reader :instrumentations def initialize @instrumentations = [] end def instrument(name, env) @instrumentations << [name, env] yield end end def options(hash = nil) Faraday::Request::Instrumentation::Options.from hash end def conn(hash = nil) hash ||= {} hash[:instrumenter] = @instrumenter Faraday.new do |f| f.request :instrumentation, hash f.adapter :test do |stub| stub.get '/' do [200, {}, 'ok'] end end end end end end faraday-0.9.2/test/middleware/retry_test.rb000066400000000000000000000153611260477344700210020ustar00rootroot00000000000000require File.expand_path("../../helper", __FILE__) module Middleware class RetryTest < Faraday::TestCase def setup @times_called = 0 @envs = [] end def conn(*retry_args) Faraday.new do |b| b.request :retry, *retry_args b.adapter :test do |stub| ['get', 'post'].each do |method| stub.send(method, '/unstable') do |env| @times_called += 1 @envs << env.dup env[:body] = nil # simulate blanking out response body @explode.call @times_called end end end end end def test_unhandled_error @explode = lambda {|n| raise "boom!" } assert_raises(RuntimeError) { conn.get("/unstable") } assert_equal 1, @times_called end def test_handled_error @explode = lambda {|n| raise Errno::ETIMEDOUT } assert_raises(Errno::ETIMEDOUT) { conn.get("/unstable") } assert_equal 3, @times_called end def test_legacy_max_retries @explode = lambda {|n| raise Errno::ETIMEDOUT } assert_raises(Errno::ETIMEDOUT) { conn(1).get("/unstable") } assert_equal 2, @times_called end def test_legacy_max_negative_retries @explode = lambda {|n| raise Errno::ETIMEDOUT } assert_raises(Errno::ETIMEDOUT) { conn(-9).get("/unstable") } assert_equal 1, @times_called end def test_new_max_retries @explode = lambda {|n| raise Errno::ETIMEDOUT } assert_raises(Errno::ETIMEDOUT) { conn(:max => 3).get("/unstable") } assert_equal 4, @times_called end def test_new_max_negative_retries @explode = lambda { |n| raise Errno::ETIMEDOUT } assert_raises(Errno::ETIMEDOUT) { conn(:max => -9).get("/unstable") } assert_equal 1, @times_called end def test_interval @explode = lambda {|n| raise Errno::ETIMEDOUT } started = Time.now assert_raises(Errno::ETIMEDOUT) { conn(:max => 2, :interval => 0.1).get("/unstable") } assert_in_delta 0.2, Time.now - started, 0.04 end def test_calls_sleep_amount explode_app = MiniTest::Mock.new explode_app.expect(:call, nil, [{:body=>nil}]) def explode_app.call(env) raise Errno::ETIMEDOUT end retry_middleware = Faraday::Request::Retry.new(explode_app) class << retry_middleware attr_accessor :sleep_amount_retries def sleep_amount(retries) self.sleep_amount_retries.delete(retries) 0 end end retry_middleware.sleep_amount_retries = [2, 1] assert_raises(Errno::ETIMEDOUT) { retry_middleware.call({:method => :get}) } assert_empty retry_middleware.sleep_amount_retries end def test_exponential_backoff middleware = Faraday::Request::Retry.new(nil, :max => 5, :interval => 0.1, :backoff_factor => 2) assert_equal middleware.sleep_amount(5), 0.1 assert_equal middleware.sleep_amount(4), 0.2 assert_equal middleware.sleep_amount(3), 0.4 end def test_exponential_backoff_with_max_interval middleware = Faraday::Request::Retry.new(nil, :max => 5, :interval => 1, :max_interval => 3, :backoff_factor => 2) assert_equal middleware.sleep_amount(5), 1 assert_equal middleware.sleep_amount(4), 2 assert_equal middleware.sleep_amount(3), 3 assert_equal middleware.sleep_amount(2), 3 end def test_random_additional_interval_amount middleware = Faraday::Request::Retry.new(nil, :max => 2, :interval => 0.1, :interval_randomness => 1.0) sleep_amount = middleware.sleep_amount(2) assert_operator sleep_amount, :>=, 0.1 assert_operator sleep_amount, :<=, 0.2 middleware = Faraday::Request::Retry.new(nil, :max => 2, :interval => 0.1, :interval_randomness => 0.5) sleep_amount = middleware.sleep_amount(2) assert_operator sleep_amount, :>=, 0.1 assert_operator sleep_amount, :<=, 0.15 middleware = Faraday::Request::Retry.new(nil, :max => 2, :interval => 0.1, :interval_randomness => 0.25) sleep_amount = middleware.sleep_amount(2) assert_operator sleep_amount, :>=, 0.1 assert_operator sleep_amount, :<=, 0.125 end def test_custom_exceptions @explode = lambda {|n| raise "boom!" } assert_raises(RuntimeError) { conn(:exceptions => StandardError).get("/unstable") } assert_equal 3, @times_called end def test_should_retry_with_body_if_block_returns_true_for_non_idempotent_request body = { :foo => :bar } @explode = lambda {|n| raise Errno::ETIMEDOUT } check = lambda { |env,exception| true } assert_raises(Errno::ETIMEDOUT) { conn(:retry_if => check).post("/unstable", body) } assert_equal 3, @times_called assert @envs.all? { |env| env[:body] === body } end def test_should_stop_retrying_if_block_returns_false_checking_env @explode = lambda {|n| raise Errno::ETIMEDOUT } check = lambda { |env,exception| env[:method] != :post } assert_raises(Errno::ETIMEDOUT) { conn(:retry_if => check).post("/unstable") } assert_equal 1, @times_called end def test_should_stop_retrying_if_block_returns_false_checking_exception @explode = lambda {|n| raise Errno::ETIMEDOUT } check = lambda { |env,exception| !exception.kind_of?(Errno::ETIMEDOUT) } assert_raises(Errno::ETIMEDOUT) { conn(:retry_if => check).post("/unstable") } assert_equal 1, @times_called end def test_should_not_call_retry_if_for_idempotent_methods_if_methods_unspecified @explode = lambda {|n| raise Errno::ETIMEDOUT } check = lambda { |env,exception| raise "this should have never been called" } assert_raises(Errno::ETIMEDOUT) { conn(:retry_if => check).get("/unstable") } assert_equal 3, @times_called end def test_should_not_retry_for_non_idempotent_method_if_methods_unspecified @explode = lambda {|n| raise Errno::ETIMEDOUT } assert_raises(Errno::ETIMEDOUT) { conn.post("/unstable") } assert_equal 1, @times_called end def test_should_not_call_retry_if_for_specified_methods @explode = lambda {|n| raise Errno::ETIMEDOUT } check = lambda { |env,exception| raise "this should have never been called" } assert_raises(Errno::ETIMEDOUT) { conn(:retry_if => check, :methods => [:post]).post("/unstable") } assert_equal 3, @times_called end def test_should_call_retry_if_for_empty_method_list @explode = lambda {|n| raise Errno::ETIMEDOUT } check = lambda { |env,exception| @times_called < 2 } assert_raises(Errno::ETIMEDOUT) { conn(:retry_if => check, :methods => []).get("/unstable") } assert_equal 2, @times_called end end end faraday-0.9.2/test/middleware_stack_test.rb000066400000000000000000000114341260477344700210170ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class MiddlewareStackTest < Faraday::TestCase # mock handler classes class Handler < Struct.new(:app) def call(env) (env[:request_headers]['X-Middleware'] ||= '') << ":#{self.class.name.split('::').last}" app.call(env) end end class Apple < Handler; end class Orange < Handler; end class Banana < Handler; end class Broken < Faraday::Middleware dependency 'zomg/i_dont/exist' end def setup @conn = Faraday::Connection.new @builder = @conn.builder end def test_sets_default_adapter_if_none_set default_middleware = Faraday::Request.lookup_middleware :url_encoded default_adapter_klass = Faraday::Adapter.lookup_middleware Faraday.default_adapter assert @builder[0] == default_middleware assert @builder[1] == default_adapter_klass end def test_allows_rebuilding build_stack Apple build_stack Orange assert_handlers %w[Orange] end def test_allows_extending build_stack Apple @conn.use Orange assert_handlers %w[Apple Orange] end def test_builder_is_passed_to_new_faraday_connection new_conn = Faraday::Connection.new :builder => @builder assert_equal @builder, new_conn.builder end def test_insert_before build_stack Apple, Orange @builder.insert_before Apple, Banana assert_handlers %w[Banana Apple Orange] end def test_insert_after build_stack Apple, Orange @builder.insert_after Apple, Banana assert_handlers %w[Apple Banana Orange] end def test_swap_handlers build_stack Apple, Orange @builder.swap Apple, Banana assert_handlers %w[Banana Orange] end def test_delete_handler build_stack Apple, Orange @builder.delete Apple 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::RackBuilder::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 def test_handler_comparison build_stack Apple assert_equal @builder.handlers.first, Apple assert_equal @builder.handlers[0,1], [Apple] assert_equal @builder.handlers.first, Faraday::RackBuilder::Handler.new(Apple) end def test_unregistered_symbol err = assert_raises(Faraday::Error){ build_stack :apple } assert_equal ":apple is not registered on Faraday::Middleware", err.message end def test_registered_symbol Faraday::Middleware.register_middleware :apple => Apple begin build_stack :apple assert_handlers %w[Apple] ensure unregister_middleware Faraday::Middleware, :apple end end def test_registered_symbol_with_proc Faraday::Middleware.register_middleware :apple => lambda { Apple } begin build_stack :apple assert_handlers %w[Apple] ensure unregister_middleware Faraday::Middleware, :apple end end def test_registered_symbol_with_array Faraday::Middleware.register_middleware File.expand_path("..", __FILE__), :strawberry => [lambda { Strawberry }, 'strawberry'] begin build_stack :strawberry assert_handlers %w[Strawberry] ensure unregister_middleware Faraday::Middleware, :strawberry end end def test_missing_dependencies build_stack Broken err = assert_raises RuntimeError do @conn.get('/') end assert_match "missing dependency for MiddlewareStackTest::Broken: ", err.message assert_match "zomg/i_dont/exist", err.message end def test_env_stored_on_middleware_response_has_reference_to_the_response env = response = nil build_stack Struct.new(:app) { define_method(:call) { |e| env, response = e, app.call(e) } } @conn.get("/") assert_same env.response, response.env.response end private # make a stack with test adapter that reflects the order of middleware def build_stack(*handlers) @builder.build do |b| handlers.each { |handler| b.use(*handler) } yield(b) if block_given? b.adapter :test do |stub| stub.get '/' do |env| # echo the "X-Middleware" request header in the body [200, {}, env[:request_headers]['X-Middleware'].to_s] end end end end def assert_handlers(list) echoed_list = @conn.get('/').body.to_s.split(':') echoed_list.shift if echoed_list.first == '' assert_equal list, echoed_list end def unregister_middleware(component, key) # TODO: unregister API? component.instance_variable_get('@registered_middleware').delete(key) end end faraday-0.9.2/test/multibyte.txt000066400000000000000000000000151260477344700167010ustar00rootroot00000000000000ファイル faraday-0.9.2/test/options_test.rb000066400000000000000000000140011260477344700172010ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class OptionsTest < Faraday::TestCase class SubOptions < Faraday::Options.new(:sub); end class ParentOptions < Faraday::Options.new(:a, :b, :c) options :c => SubOptions end def test_clear options = SubOptions.new(1) assert !options.empty? assert options.clear assert options.empty? end def test_empty options = SubOptions.new assert options.empty? options.sub = 1 assert !options.empty? options.delete(:sub) assert options.empty? end def test_each_key options = ParentOptions.new(1, 2, 3) enum = options.each_key assert_equal enum.next.to_sym, :a assert_equal enum.next.to_sym, :b assert_equal enum.next.to_sym, :c end def test_key? options = SubOptions.new assert !options.key?(:sub) options.sub = 1 if RUBY_VERSION >= '1.9' assert options.key?(:sub) else assert options.key?("sub") end end def test_each_value options = ParentOptions.new(1, 2, 3) enum = options.each_value assert_equal enum.next, 1 assert_equal enum.next, 2 assert_equal enum.next, 3 end def test_value? options = SubOptions.new assert !options.value?(1) options.sub = 1 assert options.value?(1) end def test_request_proxy_setter options = Faraday::RequestOptions.new assert_nil options.proxy assert_raises NoMethodError do options[:proxy] = {:booya => 1} end options[:proxy] = {:user => 'user'} assert_kind_of Faraday::ProxyOptions, options.proxy assert_equal 'user', options.proxy.user options.proxy = nil assert_nil options.proxy end def test_proxy_options_from_string options = Faraday::ProxyOptions.from 'http://user:pass@example.org' assert_equal 'user', options.user assert_equal 'pass', options.password assert_kind_of URI, options.uri assert_equal '', options.path assert_equal 80, options.port assert_equal 'example.org', options.host assert_equal 'http', options.scheme end def test_proxy_options_hash_access proxy = Faraday::ProxyOptions.from 'http://a%40b:pw%20d@example.org' assert_equal 'a@b', proxy[:user] assert_equal 'a@b', proxy.user assert_equal 'pw d', proxy[:password] assert_equal 'pw d', proxy.password end def test_proxy_options_no_auth proxy = Faraday::ProxyOptions.from 'http://example.org' assert_nil proxy.user assert_nil proxy.password end def test_from_options options = ParentOptions.new(1) value = ParentOptions.from(options) assert_equal 1, value.a assert_nil value.b end def test_from_options_with_sub_object sub = SubOptions.new(1) options = ParentOptions.from :a => 1, :c => sub assert_kind_of ParentOptions, options assert_equal 1, options.a assert_nil options.b assert_kind_of SubOptions, options.c assert_equal 1, options.c.sub end def test_from_hash options = ParentOptions.from :a => 1 assert_kind_of ParentOptions, options assert_equal 1, options.a assert_nil options.b end def test_from_hash_with_sub_object options = ParentOptions.from :a => 1, :c => {:sub => 1} assert_kind_of ParentOptions, options assert_equal 1, options.a assert_nil options.b assert_kind_of SubOptions, options.c assert_equal 1, options.c.sub end def test_inheritance subclass = Class.new(ParentOptions) options = subclass.from(:c => {:sub => 'hello'}) assert_kind_of SubOptions, options.c assert_equal 'hello', options.c.sub end def test_from_deep_hash hash = {:b => 1} options = ParentOptions.from :a => hash assert_equal 1, options.a[:b] hash[:b] = 2 assert_equal 1, options.a[:b] options.a[:b] = 3 assert_equal 2, hash[:b] assert_equal 3, options.a[:b] end def test_from_nil options = ParentOptions.from(nil) assert_kind_of ParentOptions, options assert_nil options.a assert_nil options.b end def test_invalid_key assert_raises NoMethodError do ParentOptions.from :invalid => 1 end end def test_update options = ParentOptions.new(1) assert_equal 1, options.a assert_nil options.b updated = options.update :a => 2, :b => 3 assert_equal 2, options.a assert_equal 3, options.b assert_equal options, updated end def test_delete options = ParentOptions.new(1) assert_equal 1, options.a assert_equal 1, options.delete(:a) assert_nil options.a end def test_merge options = ParentOptions.new(1) assert_equal 1, options.a assert_nil options.b dup = options.merge :a => 2, :b => 3 assert_equal 2, dup.a assert_equal 3, dup.b assert_equal 1, options.a assert_nil options.b end def test_env_access_member e = Faraday::Env.new assert_nil e.method e.method = :get assert_equal :get, e.method end def test_env_access_symbol_non_member e = Faraday::Env.new assert_nil e[:custom] e[:custom] = :boom assert_equal :boom, e[:custom] end def test_env_access_string_non_member e = Faraday::Env.new assert_nil e["custom"] e["custom"] = :boom assert_equal :boom, e["custom"] end def test_env_fetch_ignores_false ssl = Faraday::SSLOptions.new ssl.verify = false assert !ssl.fetch(:verify, true) end def test_fetch_grabs_value opt = Faraday::SSLOptions.new opt.verify = 1 assert_equal 1, opt.fetch(:verify, false) { |k| :blah } end def test_fetch_uses_falsey_default opt = Faraday::SSLOptions.new assert_equal false, opt.fetch(:verify, false) { |k| :blah } end def test_fetch_accepts_block opt = Faraday::SSLOptions.new assert_equal "yo :verify", opt.fetch(:verify) { |k| "yo #{k.inspect}"} end def test_fetch_needs_a_default_if_key_is_missing opt = Faraday::SSLOptions.new assert_raises Faraday::Options.fetch_error_class do opt.fetch :verify end end def test_fetch_works_with_key opt = Faraday::SSLOptions.new opt.verify = 1 assert_equal 1, opt.fetch(:verify) end end faraday-0.9.2/test/parameters_test.rb000066400000000000000000000107701260477344700176620ustar00rootroot00000000000000require File.expand_path("../helper", __FILE__) require "rack/utils" class TestParameters < Faraday::TestCase # emulates ActiveSupport::SafeBuffer#gsub FakeSafeBuffer = Struct.new(:string) do def to_s() self end def gsub(regex) string.gsub(regex) { match, = $&, '' =~ /a/ yield(match) } end end def test_escaping_safe_buffer_nested monies = FakeSafeBuffer.new("$32,000.00") assert_equal "a=%2432%2C000.00", Faraday::NestedParamsEncoder.encode("a" => monies) end def test_escaping_safe_buffer_flat monies = FakeSafeBuffer.new("$32,000.00") assert_equal "a=%2432%2C000.00", Faraday::FlatParamsEncoder.encode("a" => monies) end def test_raises_typeerror_nested error = assert_raises TypeError do Faraday::NestedParamsEncoder.encode("") end assert_equal "Can't convert String into Hash.", error.message end def test_raises_typeerror_flat error = assert_raises TypeError do Faraday::FlatParamsEncoder.encode("") end assert_equal "Can't convert String into Hash.", error.message end def test_decode_array_nested query = "a[1]=one&a[2]=two&a[3]=three" expected = {"a" => ["one", "two", "three"]} assert_equal expected, Faraday::NestedParamsEncoder.decode(query) end def test_decode_array_flat query = "a=one&a=two&a=three" expected = {"a" => ["one", "two", "three"]} assert_equal expected, Faraday::FlatParamsEncoder.decode(query) end def test_nested_decode_hash query = "a[b1]=one&a[b2]=two&a[b][c]=foo" expected = {"a" => {"b1" => "one", "b2" => "two", "b" => {"c" => "foo"}}} assert_equal expected, Faraday::NestedParamsEncoder.decode(query) end def test_encode_nil_nested assert_equal "a", Faraday::NestedParamsEncoder.encode("a" => nil) end def test_encode_nil_flat assert_equal "a", Faraday::FlatParamsEncoder.encode("a" => nil) end def test_decode_nested_array_rack_compat query = "a[][one]=1&a[][two]=2&a[][one]=3&a[][two]=4" expected = Rack::Utils.parse_nested_query(query) assert_equal expected, Faraday::NestedParamsEncoder.decode(query) end def test_decode_nested_array_mixed_types query = "a[][one]=1&a[]=2&a[]=&a[]" expected = Rack::Utils.parse_nested_query(query) assert_equal expected, Faraday::NestedParamsEncoder.decode(query) end def test_decode_nested_ignores_invalid_array query = "[][a]=1&b=2" expected = {"a" => "1", "b" => "2"} assert_equal expected, Faraday::NestedParamsEncoder.decode(query) end def test_decode_nested_ignores_repeated_array_notation query = "a[][][]=1" expected = {"a" => ["1"]} assert_equal expected, Faraday::NestedParamsEncoder.decode(query) end def test_decode_nested_ignores_malformed_keys query = "=1&[]=2" expected = {} assert_equal expected, Faraday::NestedParamsEncoder.decode(query) end def test_decode_nested_subkeys_dont_have_to_be_in_brackets query = "a[b]c[d]e=1" expected = {"a" => {"b" => {"c" => {"d" => {"e" => "1"}}}}} assert_equal expected, Faraday::NestedParamsEncoder.decode(query) end def test_decode_nested_raises_error_when_expecting_hash error = assert_raises TypeError do Faraday::NestedParamsEncoder.decode("a=1&a[b]=2") end assert_equal "expected Hash (got String) for param `a'", error.message error = assert_raises TypeError do Faraday::NestedParamsEncoder.decode("a[]=1&a[b]=2") end assert_equal "expected Hash (got Array) for param `a'", error.message error = assert_raises TypeError do Faraday::NestedParamsEncoder.decode("a[b]=1&a[]=2") end assert_equal "expected Array (got Hash) for param `a'", error.message error = assert_raises TypeError do Faraday::NestedParamsEncoder.decode("a=1&a[]=2") end assert_equal "expected Array (got String) for param `a'", error.message error = assert_raises TypeError do Faraday::NestedParamsEncoder.decode("a[b]=1&a[b][c]=2") end assert_equal "expected Hash (got String) for param `b'", error.message end def test_decode_nested_final_value_overrides_any_type query = "a[b][c]=1&a[b]=2" expected = {"a" => {"b" => "2"}} assert_equal expected, Faraday::NestedParamsEncoder.decode(query) end def test_encode_rack_compat_nested params = { :a => [{:one => "1", :two => "2"}, "3", ""] } expected = Rack::Utils.build_nested_query(params) assert_equal expected.split("&").sort, Faraday::Utils.unescape(Faraday::NestedParamsEncoder.encode(params)).split("&").sort end end faraday-0.9.2/test/request_middleware_test.rb000066400000000000000000000112151260477344700213770ustar00rootroot00000000000000# encoding: utf-8 require File.expand_path('../helper', __FILE__) Faraday::CompositeReadIO.class_eval { attr_reader :ios } class RequestMiddlewareTest < Faraday::TestCase def setup @conn = Faraday.new do |b| b.request :multipart b.request :url_encoded b.adapter :test do |stub| stub.post('/echo') do |env| posted_as = env[:request_headers]['Content-Type'] [200, {'Content-Type' => posted_as}, env[:body]] end end end end def with_utf8 if defined?(RUBY_VERSION) && RUBY_VERSION.match(/1.8.\d/) begin previous_kcode = $KCODE $KCODE = "UTF8" yield ensure $KCODE = previous_kcode end else yield end end def test_does_nothing_without_payload response = @conn.post('/echo') assert_nil response.headers['Content-Type'] assert response.body.empty? end def test_ignores_custom_content_type response = @conn.post('/echo', { :some => 'data' }, 'content-type' => 'application/x-foo') assert_equal 'application/x-foo', response.headers['Content-Type'] assert_equal({ :some => 'data' }, response.body) end def test_url_encoded_no_header response = @conn.post('/echo', { :fruit => %w[apples oranges] }) assert_equal 'application/x-www-form-urlencoded', response.headers['Content-Type'] assert_equal 'fruit%5B%5D=apples&fruit%5B%5D=oranges', response.body end def test_url_encoded_with_header response = @conn.post('/echo', {'a'=>123}, 'content-type' => 'application/x-www-form-urlencoded') assert_equal 'application/x-www-form-urlencoded', response.headers['Content-Type'] assert_equal 'a=123', response.body end def test_url_encoded_nested response = @conn.post('/echo', { :user => {:name => 'Mislav', :web => 'mislav.net'} }) assert_equal 'application/x-www-form-urlencoded', response.headers['Content-Type'] expected = { 'user' => {'name' => 'Mislav', 'web' => 'mislav.net'} } assert_equal expected, Faraday::Utils.parse_nested_query(response.body) end def test_url_encoded_non_nested response = @conn.post('/echo', { :dimensions => ['date', 'location']}) do |req| req.options.params_encoder = Faraday::FlatParamsEncoder end assert_equal 'application/x-www-form-urlencoded', response.headers['Content-Type'] expected = { 'dimensions' => ['date', 'location'] } assert_equal expected, Faraday::Utils.parse_query(response.body) assert_equal 'dimensions=date&dimensions=location', response.body end def test_url_encoded_unicode err = capture_warnings { response = @conn.post('/echo', {:str => "eé cç aã aâ"}) assert_equal "str=e%C3%A9+c%C3%A7+a%C3%A3+a%C3%A2", response.body } assert err.empty?, "stderr did include: #{err}" end def test_url_encoded_unicode_with_kcode_set with_utf8 do err = capture_warnings { response = @conn.post('/echo', {:str => "eé cç aã aâ"}) assert_equal "str=e%C3%A9+c%C3%A7+a%C3%A3+a%C3%A2", response.body } assert err.empty?, "stderr did include: #{err}" end end def test_url_encoded_nested_keys response = @conn.post('/echo', {'a'=>{'b'=>{'c'=>['d']}}}) assert_equal "a%5Bb%5D%5Bc%5D%5B%5D=d", response.body end def test_multipart # assume params are out of order regexes = [ /name\=\"a\"/, /name=\"b\[c\]\"\; filename\=\"request_middleware_test\.rb\"/, /name=\"b\[d\]\"/] payload = {:a => 1, :b => {:c => Faraday::UploadIO.new(__FILE__, 'text/x-ruby'), :d => 2}} response = @conn.post('/echo', payload) assert_kind_of Faraday::CompositeReadIO, response.body assert_equal "multipart/form-data; boundary=%s" % Faraday::Request::Multipart::DEFAULT_BOUNDARY, response.headers['Content-Type'] response.body.send(:ios).map{|io| io.read}.each do |io| if re = regexes.detect { |r| io =~ r } regexes.delete re end end assert_equal [], regexes end def test_multipart_with_arrays # assume params are out of order regexes = [ /name\=\"a\"/, /name=\"b\[\]\[c\]\"\; filename\=\"request_middleware_test\.rb\"/, /name=\"b\[\]\[d\]\"/] payload = {:a => 1, :b =>[{:c => Faraday::UploadIO.new(__FILE__, 'text/x-ruby'), :d => 2}]} response = @conn.post('/echo', payload) assert_kind_of Faraday::CompositeReadIO, response.body assert_equal "multipart/form-data; boundary=%s" % Faraday::Request::Multipart::DEFAULT_BOUNDARY, response.headers['Content-Type'] response.body.send(:ios).map{|io| io.read}.each do |io| if re = regexes.detect { |r| io =~ r } regexes.delete re end end assert_equal [], regexes end end faraday-0.9.2/test/response_middleware_test.rb000066400000000000000000000035501260477344700215500ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class ResponseMiddlewareTest < Faraday::TestCase def setup @conn = Faraday.new do |b| b.response :raise_error b.adapter :test do |stub| stub.get('ok') { [200, {'Content-Type' => 'text/html'}, ''] } stub.get('not-found') { [404, {'X-Reason' => 'because'}, 'keep looking'] } stub.get('error') { [500, {'X-Error' => 'bailout'}, 'fail'] } end end end class ResponseUpcaser < Faraday::Response::Middleware def parse(body) body.upcase end end def test_success assert @conn.get('ok') end def test_raises_not_found error = assert_raises Faraday::Error::ResourceNotFound do @conn.get('not-found') end assert_equal 'the server responded with status 404', error.message assert_equal 'because', error.response[:headers]['X-Reason'] end def test_raises_error error = assert_raises Faraday::Error::ClientError do @conn.get('error') end assert_equal 'the server responded with status 500', error.message assert_equal 'bailout', error.response[:headers]['X-Error'] end def test_upcase @conn.builder.insert(0, ResponseUpcaser) assert_equal '', @conn.get('ok').body end end class ResponseNoBodyMiddleWareTest < Faraday::TestCase def setup @conn = Faraday.new do |b| b.response :raise_error b.adapter :test do |stub| stub.get('not_modified') { [304, nil, nil] } stub.get('no_content') { [204, nil, nil] } end end @conn.builder.insert(0, NotCalled) end class NotCalled < Faraday::Response::Middleware def parse(body) raise "this should not be called" end end def test_204 assert_equal nil, @conn.get('no_content').body end def test_304 assert_equal nil, @conn.get('not_modified').body end end faraday-0.9.2/test/strawberry.rb000066400000000000000000000001111260477344700166500ustar00rootroot00000000000000class MiddlewareStackTest::Strawberry < MiddlewareStackTest::Handler end faraday-0.9.2/test/utils_test.rb000066400000000000000000000030271260477344700166540ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class TestUtils < Faraday::TestCase def setup @url = "http://example.com/abc" end # emulates ActiveSupport::SafeBuffer#gsub FakeSafeBuffer = Struct.new(:string) do def to_s() self end def gsub(regex) string.gsub(regex) { match, = $&, '' =~ /a/ yield(match) } end end def test_escaping_safe_buffer str = FakeSafeBuffer.new('$32,000.00') assert_equal '%2432%2C000.00', Faraday::Utils.escape(str) end def test_parses_with_default with_default_uri_parser(nil) do uri = normalize(@url) assert_equal 'example.com', uri.host end end def test_parses_with_URI with_default_uri_parser(::URI) do uri = normalize(@url) assert_equal 'example.com', uri.host end end def test_parses_with_block with_default_uri_parser(lambda {|u| "booya#{"!" * u.size}" }) do assert_equal 'booya!!!!!!!!!!!!!!!!!!!!!!', normalize(@url) end end def test_replace_header_hash headers = Faraday::Utils::Headers.new('authorization' => 't0ps3cr3t!') assert headers.include?('authorization') headers.replace({'content-type' => 'text/plain'}) assert !headers.include?('authorization') end def normalize(url) Faraday::Utils::URI(url) end def with_default_uri_parser(parser) old_parser = Faraday::Utils.default_uri_parser begin Faraday::Utils.default_uri_parser = parser yield ensure Faraday::Utils.default_uri_parser = old_parser end end end