pax_global_header00006660000000000000000000000064134265674340014530gustar00rootroot0000000000000052 comment=08f49198f122deab69e16216165aeb3bac8c2d71 ruby-faraday-0.15.4/000077500000000000000000000000001342656743400142055ustar00rootroot00000000000000ruby-faraday-0.15.4/.document000066400000000000000000000001011342656743400160140ustar00rootroot00000000000000LICENSE.md README.md bin/* lib/**/*.rb test/**/*.rb .github/*.md ruby-faraday-0.15.4/.github/000077500000000000000000000000001342656743400155455ustar00rootroot00000000000000ruby-faraday-0.15.4/.github/CODE_OF_CONDUCT.md000066400000000000000000000062401342656743400203460ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainer at giuffrida.mattia AT gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ruby-faraday-0.15.4/.github/CONTRIBUTING.md000066400000000000000000000030341342656743400177760ustar00rootroot00000000000000## 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 # setup development dependencies $ script/bootstrap # run the whole suite $ script/test # run only specific files $ script/test excon patron # run tests using SSL $ SSL=yes script/test ``` ### New Features When adding a feature Faraday: 1. also add tests to cover your new feature. 2. if the feature is for an adapter, the **attempt** must be made to add the same feature to all other adapters as well. 3. start opening an issue describing how the new feature will work, and only after receiving the green light by the core team start working on the PR. ### New Middlewares 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. ### New Adapters 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/releases [faraday_middleware]: https://github.com/lostisland/faraday_middleware/wiki ruby-faraday-0.15.4/.github/ISSUE_TEMPLATE.md000066400000000000000000000014401342656743400202510ustar00rootroot00000000000000## Basic Info * Faraday Version: * Ruby Version: ## Issue description Please provide a description of the issue you're experiencing. Please also provide the exception message/stacktrace or any other useful detail. ## Steps to reproduce If possible, please provide the steps to reproduce the issue. ## CHECKLIST (delete before creating the issue) * If you're not reporting a bug/issue, you can ignore this whole template. * Are you using the latest Faraday version? If not, please check the [Releases](https://github.com/lostisland/faraday/releases) page to see if the issue has already been fixed. * Provide the Faraday and Ruby Version you're using while experiencing the issue. * Fill the `Issue description` and `Steps to Reproduce` sections. * Delete this checklist before posting the issue. ruby-faraday-0.15.4/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000004201342656743400213420ustar00rootroot00000000000000## Description A few sentences describing the overall goals of the pull request's commits. Link to related issues if any. (As `Fixes #XXX`) ## Todos List any remaining work that needs to be done, i.e: - [ ] Tests - [ ] Documentation ## Additional Notes Optional section ruby-faraday-0.15.4/.gitignore000066400000000000000000000002551342656743400161770ustar00rootroot00000000000000## PROJECT::GENERAL coverage rdoc doc log pkg/* tmp .rvmrc .ruby-version ## BUNDLER bin *.gem .bundle Gemfile.lock vendor/bundle ## PROJECT::SPECIFIC .rbx ## IDEs .idea/ ruby-faraday-0.15.4/.travis.yml000066400000000000000000000016431342656743400163220ustar00rootroot00000000000000sudo: false language: ruby script: bundle exec script/test cache: bundler rvm: - 1.9.3 - 2.0.0 - 2.1 - 2.2 - 2.3 - 2.4 - 2.5.0 - ruby-head - jruby-19mode - jruby-20mode - jruby-21mode - jruby-head matrix: allow_failures: # "A fatal error has been detected by the Java Runtime Environment: # Internal Error (sharedRuntime.cpp:843)" - rvm: jruby-19mode - rvm: jruby-20mode - rvm: jruby-21mode - rvm: jruby-head - rvm: ruby-head fast_finish: true env: matrix: - SSL=no - SSL=yes global: - JRUBY_OPTS="$JRUBY_OPTS --debug" deploy: provider: rubygems api_key: secure: EqbOu9BQp5jkivJ8qvTo89f3J49KOByBueU3XulrJ2Kqm/ov4RDFsmp9/uHAnSLdmKSkzZaeq79t1AUNfKGX1ZqkKgq/Nw2BKGFnh5ZOjrkrRZR1Vm09OHxqiViEbtg+jZ8VOLY/iDFEkNIzuj9/H3iHGXC0XiKH2LTHOFH63Bs= gem: faraday on: tags: true repo: lostisland/faraday rvm: 2.4 condition: '"$SSL" = "yes"' ruby-faraday-0.15.4/CHANGELOG.md000066400000000000000000000017761342656743400160310ustar00rootroot00000000000000# Faraday Changelog For newer changes, please see https://github.com/lostisland/faraday/releases ## 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) ruby-faraday-0.15.4/Gemfile000066400000000000000000000015151342656743400155020ustar00rootroot00000000000000source 'https://rubygems.org' ruby RUBY_VERSION 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 'addressable', '< 2.4.0' 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' 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', '~> 1.3', :require => 'typhoeus' end gemspec ruby-faraday-0.15.4/LICENSE.md000066400000000000000000000020601342656743400156070ustar00rootroot00000000000000Copyright (c) 2009-2017 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. ruby-faraday-0.15.4/README.md000066400000000000000000000304161342656743400154700ustar00rootroot00000000000000# Faraday [![Gem Version](https://badge.fury.io/rb/faraday.svg)](https://rubygems.org/gems/faraday) [![Build Status](https://travis-ci.org/lostisland/faraday.svg)](https://travis-ci.org/lostisland/faraday) [![Coverage Status](https://coveralls.io/repos/github/lostisland/faraday/badge.svg?branch=master)](https://coveralls.io/github/lostisland/faraday?branch=master) [![Code Climate](https://codeclimate.com/github/lostisland/faraday/badges/gpa.svg)](https://codeclimate.com/github/lostisland/faraday) [![Gitter](https://badges.gitter.im/lostisland/faraday.svg)](https://gitter.im/lostisland/faraday?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 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 out of the box: * [Net::HTTP][net_http] _(default)_ * [Net::HTTP::Persistent][persistent] * [Excon][] * [Patron][] * [EventMachine][] * [HTTPClient][] Adapters are slowly being moved into their own gems, or bundled with HTTP clients: * [Typhoeus][] It also includes a Rack adapter for hitting loaded Rack applications through Rack::Test, and a Test adapter for stubbing requests by hand. ## API documentation Available at [rubydoc.info](http://www.rubydoc.info/gems/faraday). ## Usage ### Basic Use ```ruby response = Faraday.get 'http://sushi.com/nigiri/sake.json' ``` A simple `get` request can be performed by using the syntax described above. This works 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)). A more flexible way to use Faraday is to start with a Connection object. If you want to keep the same defaults, you can use this syntax: ```ruby conn = Faraday.new(:url => 'http://www.example.com') response = conn.get '/users' # GET http://www.example.com/users' ``` Connections can also take an options hash as a parameter or be configured by using a block. Checkout the section called [Advanced middleware usage](#advanced-middleware-usage) for more details about how to use this block for configurations. Since the default middleware stack uses url\_encoded middleware and default adapter, use them on building your own middleware stack. ```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 # Filter sensitive information from logs with a regex matcher conn = Faraday.new(:url => 'http://sushi.com/api_key=s3cr3t') do |faraday| faraday.request :url_encoded # form-encode POST params faraday.response :logger do | logger | logger.filter(/(api_key=)(\w+)/,'\1[REMOVED]') end faraday.adapter Faraday.default_adapter # make requests with Net::HTTP end ``` Once you have the connection object, use it to make HTTP requests. You can pass parameters to it in a few different ways: ```ruby ## 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 ``` Some configuration options can be adjusted per request: ```ruby # 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 ``` And you can inject arbitrary data into the request using the `context` option: ```ruby # Anything you inject using context option will be available in the env on all middlewares conn.get do |req| req.url '/search' req.options.context = { foo: 'foo', bar: 'bar' } end ``` ### 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 :request => { :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. ## Authentication Basic and Token authentication are handled by Faraday::Request::BasicAuthentication and Faraday::Request::TokenAuthentication respectively. These can be added as middleware manually or through the helper methods. ```ruby Faraday.new(...) do |conn| conn.basic_auth('username', 'password') end Faraday.new(...) do |conn| conn.token_auth('authentication-token') end ``` ## Proxy Faraday will try to automatically infer the proxy settings from your system using `URI#find_proxy`. This will retrieve them from environment variables such as http_proxy, ftp_proxy, no_proxy, etc. If for any reason you want to disable this behaviour, you can do so by setting the global varibale `ignore_env_proxy`: ```ruby Faraday.ignore_env_proxy = true ``` You can also specify a custom proxy when initializing the connection ```ruby Faraday.new('http://www.example.com', :proxy => 'http://proxy.com') ``` ## 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 # Last middleware must be the adapter: 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 ``` ## Ad-hoc adapters customization Faraday is intended to be a generic interface between your code and the adapter. However, sometimes you need to access a feature specific to one of the adapters that is not covered in Faraday's interface. When that happens, you can pass a block when specifying the adapter to customize it. The block parameter will change based on the adapter you're using. See below for some examples. ### NetHttp ```ruby conn = Faraday.new(...) do |f| f.adapter :net_http do |http| # yields Net::HTTP http.idle_timeout = 100 http.verify_callback = lambda do | preverify_ok, cert_store | # do something here... end end end ``` ### NetHttpPersistent ```ruby conn = Faraday.new(...) do |f| f.adapter :net_http_persistent, pool_size: 5 do |http| # yields Net::HTTP::Persistent http.idle_timeout = 100 http.retry_change_requests = true end end ``` ### Patron ```ruby conn = Faraday.new(...) do |f| f.adapter :patron do |session| # yields Patron::Session session.max_redirects = 10 end end ``` ### HTTPClient ```ruby conn = Faraday.new(...) do |f| f.adapter :httpclient do |client| # yields HTTPClient client.keep_alive_timeout = 20 client.ssl_config.timeout = 25 end end ``` ## 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 ``` ## Supported Ruby versions This library aims to support and is [tested against][travis] the following Ruby implementations: * Ruby 1.9.3+ * [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. ## Contribute Do you want to contribute to Faraday? Open the issues page and check for the `help wanted` label! But before you start coding, please read our [Contributing Guide](https://github.com/lostisland/faraday/blob/master/.github/CONTRIBUTING.md) ## Copyright Copyright (c) 2009-2017 [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]: https://travis-ci.org/lostisland/faraday [excon]: https://github.com/excon/excon#readme [patron]: http://toland.github.io/patron/ [eventmachine]: https://github.com/igrigorik/em-http-request#readme [httpclient]: https://github.com/nahi/httpclient [typhoeus]: https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb [jruby]: http://jruby.org/ [rubinius]: http://rubini.us/ [license]: LICENSE.md ruby-faraday-0.15.4/Rakefile000066400000000000000000000001551342656743400156530ustar00rootroot00000000000000require 'rake/testtask' task :default => :test desc "Run all tests" task :test do exec 'script/test' end ruby-faraday-0.15.4/faraday.gemspec000066400000000000000000000011361342656743400171620ustar00rootroot00000000000000lib = "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.required_ruby_version = '>= 1.9' spec.add_dependency 'multipart-post', '>= 1.2', '< 3' spec.files = `git ls-files -z lib LICENSE.md README.md`.split("\0") end ruby-faraday-0.15.4/lib/000077500000000000000000000000001342656743400147535ustar00rootroot00000000000000ruby-faraday-0.15.4/lib/faraday.rb000066400000000000000000000164121342656743400167130ustar00rootroot00000000000000require '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.15.4" 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: Tells faraday to ignore the environment proxy (http_proxy). attr_accessor :ignore_env_proxy # 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 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 def respond_to?(symbol, include_private = false) default_connection.respond_to?(symbol, include_private) || super end 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.ignore_env_proxy = false 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(default_connection_options) 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 # Public: Sets the default options used when calling Faraday#new. def self.default_connection_options=(options) @default_connection = nil @default_connection_options = ConnectionOptions.from(options) 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 ruby-faraday-0.15.4/lib/faraday/000077500000000000000000000000001342656743400163625ustar00rootroot00000000000000ruby-faraday-0.15.4/lib/faraday/adapter.rb000066400000000000000000000032641342656743400203340ustar00rootroot00000000000000module 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 initialize(app = nil, opts = {}, &block) super(app) @connection_options = opts @config_block = block end def call(env) env.clear_body if env.needs_body? end private def save_response(env, status, body, headers = nil, reason_phrase = nil) env.status = status env.body = body env.reason_phrase = reason_phrase && reason_phrase.to_s.strip 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 ruby-faraday-0.15.4/lib/faraday/adapter/000077500000000000000000000000001342656743400200025ustar00rootroot00000000000000ruby-faraday-0.15.4/lib/faraday/adapter/em_http.rb000066400000000000000000000157531342656743400220020ustar00rootroot00000000000000module 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 = create_request(env) req.setup_request(env[:method], request_config(env)).callback { |client| status = client.response_header.status reason = client.response_header.http_reason save_response(env, status, client.response, nil, reason) do |resp_headers| client.response_header.each do |name, value| resp_headers[name.to_sym] = value end end } end def create_request(env) EventMachine::HttpRequest.new(env[:url], connection_config(env).merge(@connection_options)) 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? ruby-faraday-0.15.4/lib/faraday/adapter/em_http_ssl_patch.rb000066400000000000000000000024621342656743400240330ustar00rootroot00000000000000require '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.uri.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) ruby-faraday-0.15.4/lib/faraday/adapter/em_synchrony.rb000066400000000000000000000060131342656743400230440ustar00rootroot00000000000000require '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 = create_request(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 status = client.response_header.status reason = client.response_header.http_reason save_response(env, status, client.response, nil, reason) 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 def create_request(env) EventMachine::HttpRequest.new(Utils::URI(env[:url].to_s), connection_config(env).merge(@connection_options)) 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? ruby-faraday-0.15.4/lib/faraday/adapter/em_synchrony/000077500000000000000000000000001342656743400225175ustar00rootroot00000000000000ruby-faraday-0.15.4/lib/faraday/adapter/em_synchrony/parallel_manager.rb000066400000000000000000000031271342656743400263350ustar00rootroot00000000000000module 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 ruby-faraday-0.15.4/lib/faraday/adapter/excon.rb000066400000000000000000000053141342656743400214460ustar00rootroot00000000000000module Faraday class Adapter class Excon < Faraday::Adapter dependency 'excon' 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] opts[:ssl_version] = ssl[:version] if ssl[:version] opts[:ssl_min_version] = ssl[:min_version] if ssl[:min_version] opts[:ssl_max_version] = ssl[:max_version] if ssl[:max_version] # 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] 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 = create_connection(env, opts) 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, resp.reason_phrase) @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 def create_connection(env, opts) ::Excon.new(env[:url].to_s, opts.merge(@connection_options)) end # TODO: support streaming requests def read_body(env) env[:body].respond_to?(:read) ? env[:body].read : env[:body] end end end end ruby-faraday-0.15.4/lib/faraday/adapter/httpclient.rb000066400000000000000000000073441342656743400225150ustar00rootroot00000000000000module 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 configure_client # 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, resp.reason @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, IOError, SocketError 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 configure_client @config_block.call(client) if @config_block end def ssl_cert_store(ssl) return ssl[:cert_store] if ssl[:cert_store] # Memoize the cert store so that the same one is passed to # HTTPClient each time, to avoid resyncing SSL sesions when # it's changed @cert_store ||= begin # 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 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 ruby-faraday-0.15.4/lib/faraday/adapter/net_http.rb000066400000000000000000000116731342656743400221640ustar00rootroot00000000000000begin 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 = [ IOError, Errno::ECONNABORTED, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH, Errno::EINVAL, Errno::ENETUNREACH, Errno::EPIPE, 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 initialize(app = nil, opts = {}, &block) @cert_store = nil super(app, opts, &block) end def call(env) super with_net_http_connection(env) do |http| configure_ssl(http, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl] configure_request(http, env[:request]) 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 || '', nil, http_response.message) 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 private 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].hostname, proxy[:uri].port, proxy[:user], proxy[:password]) else Net::HTTP end.new(env[:url].hostname, 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] http.min_version = ssl[:min_version] if ssl[:min_version] http.max_version = ssl[:max_version] if ssl[:max_version] end def configure_request(http, req) if req[:timeout] http.read_timeout = req[:timeout] http.open_timeout = req[:timeout] http.write_timeout = req[:timeout] if http.respond_to?(:write_timeout=) end http.open_timeout = req[:open_timeout] if req[:open_timeout] http.write_timeout = req[:write_timeout] if req[:write_timeout] && http.respond_to?(:write_timeout=) # Only set if Net::Http supports it, since Ruby 2.5. http.max_retries = 0 if http.respond_to?(:max_retries=) @config_block.call(http) if @config_block end def ssl_cert_store(ssl) return ssl[:cert_store] if ssl[:cert_store] return @cert_store if @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 ruby-faraday-0.15.4/lib/faraday/adapter/net_http_persistent.rb000066400000000000000000000045261342656743400244430ustar00rootroot00000000000000module Faraday class Adapter class NetHttpPersistent < NetHttp dependency 'net/http/persistent' private def net_http_connection(env) @cached_connection ||= if Net::HTTP::Persistent.instance_method(:initialize).parameters.first == [:key, :name] options = {name: 'Faraday'} options[:pool_size] = @connection_options[:pool_size] if @connection_options.key?(:pool_size) Net::HTTP::Persistent.new(options) else Net::HTTP::Persistent.new('Faraday') end proxy_uri = proxy_uri(env) @cached_connection.proxy = proxy_uri if @cached_connection.proxy_uri != proxy_uri @cached_connection end def proxy_uri(env) proxy_uri = nil 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 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_set(http, :verify_mode, ssl_verify_mode(ssl)) http_set(http, :cert_store, ssl_cert_store(ssl)) http_set(http, :certificate, ssl[:client_cert]) if ssl[:client_cert] http_set(http, :private_key, ssl[:client_key]) if ssl[:client_key] http_set(http, :ca_file, ssl[:ca_file]) if ssl[:ca_file] http_set(http, :ssl_version, ssl[:version]) if ssl[:version] end def http_set(http, attr, value) if http.send(attr) != value http.send("#{attr}=", value) end end end end end ruby-faraday-0.15.4/lib/faraday/adapter/patron.rb000066400000000000000000000063021342656743400216330ustar00rootroot00000000000000module Faraday class Adapter class Patron < Faraday::Adapter dependency 'patron' def call(env) super # TODO: support streaming requests env[:body] = env[:body].read if env[:body].respond_to? :read session = ::Patron::Session.new @config_block.call(session) if @config_block configure_ssl(session, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl] 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 # Remove the "HTTP/1.1 200", leaving just the reason phrase reason_phrase = response.status_line.gsub(/^.* \d{3} /, '') save_response(env, response.status, response.body, response.headers, reason_phrase) @app.call env rescue ::Patron::TimeoutError => err if connection_timed_out_message?(err.message) 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 configure_ssl(session, ssl) if ssl.fetch(:verify, true) session.cacert = ssl[:ca_file] else session.insecure = true end end private CURL_TIMEOUT_MESSAGES = [ "Connection time-out", "Connection timed out", "Timed out before name resolve", "server connect has timed out", "Resolving timed out", "name lookup timed out", "timed out before SSL", "connect() timed out" ].freeze def connection_timed_out_message?(message) CURL_TIMEOUT_MESSAGES.any? { |curl_message| message.include?(curl_message) } end end end end ruby-faraday-0.15.4/lib/faraday/adapter/rack.rb000066400000000000000000000031651342656743400212540ustar00rootroot00000000000000module 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 ruby-faraday-0.15.4/lib/faraday/adapter/test.rb000066400000000000000000000147751342656743400213240ustar00rootroot00000000000000module Faraday class Adapter # Examples # # test = Faraday::Connection.new do # use Faraday::Adapter::Test do |stub| # # simply define matcher to match the request # stub.get '/resource.json' do # # return static content # [200, {'Content-Type' => 'application/json'}, 'hi world'] # end # # # response with content generated based on request # stub.get '/showget' do |env| # [200, {'Content-Type' => 'text/plain'}, env[:method].to_s] # end # # # regular expression can be used as matching filter # stub.get /\A\/items\/(\d+)\z/ do |env, meta| # # in case regular expression is used an instance of MatchData can be received # [200, {'Content-Type' => 'text/plain'}, "showing item: #{meta[:match_data][1]}"] # end # end # end # # resp = test.get '/resource.json' # resp.body # => 'hi world' # # resp = test.get '/showget' # resp.body # => 'get' # # resp = test.get '/items/1' # resp.body # => 'showing item: 1' # # resp = test.get '/items/2' # resp.body # => 'showing item: 2' # 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, host, path, headers, body) return false if !@stack.key?(request_method) stack = @stack[request_method] consumed = (@consumed[request_method] ||= []) stub, meta = matches?(stack, host, path, headers, body) if stub consumed << stack.delete(stub) return stub, meta end matches?(consumed, host, path, headers, body) 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, host = if path.is_a?(Regexp) path else [Faraday::Utils.normalize_path(path), Faraday::Utils.URI(path).host] end (@stack[request_method] ||= []) << Stub.new(host, normalized_path, headers, body, block) end def matches?(stack, host, path, headers, body) stack.each do |stub| match_result, meta = stub.matches?(host, path, headers, body) return stub, meta if match_result end nil end end class Stub < Struct.new(:host, :path, :params, :headers, :body, :block) def initialize(host, full, headers, body, block) path, query = full.respond_to?(:split) ? full.split("?") : full params = query ? Faraday::Utils.parse_nested_query(query) : {} super(host, path, params, headers, body, block) end def matches?(request_host, request_uri, request_headers, request_body) request_path, request_query = request_uri.split('?') request_params = request_query ? Faraday::Utils.parse_nested_query(request_query) : {} # meta is a hash use as carrier # that will be yielded to consumer block meta = {} return (host.nil? || host == request_host) && path_match?(request_path, meta) && params_match?(request_params) && (body.to_s.size.zero? || request_body == body) && headers_match?(request_headers), meta end def path_match?(request_path, meta) if path.is_a? Regexp !!(meta[:match_data] = path.match(request_path)) else path == request_path end 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 host = env[:url].host normalized_path = Faraday::Utils.normalize_path(env[:url]) params_encoder = env.request.params_encoder || Faraday::Utils.default_params_encoder stub, meta = stubs.match(env[:method], host, normalized_path, env.request_headers, env[:body]) if stub env[:params] = (query = env[:url].query) ? params_encoder.decode(query) : {} block_arity = stub.block.arity status, headers, body = (block_arity >= 0) ? stub.block.call(*[env, meta].take(block_arity)) : stub.block.call(env, meta) 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 ruby-faraday-0.15.4/lib/faraday/adapter/typhoeus.rb000066400000000000000000000006151342656743400222110ustar00rootroot00000000000000module Faraday class Adapter # This class is just a stub, the real adapter is in https://github.com/philsturgeon/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb class Typhoeus < Faraday::Adapter # Needs to define this method in order to support Typhoeus <= 1.3.0 def call; end dependency 'typhoeus' dependency 'typhoeus/adapters/faraday' end end end ruby-faraday-0.15.4/lib/faraday/autoload.rb000066400000000000000000000046551342656743400205310ustar00rootroot00000000000000module 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', :EMSynchrony => 'em_synchrony', :EMHttp => 'em_http', :Typhoeus => 'typhoeus', :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 ruby-faraday-0.15.4/lib/faraday/connection.rb000066400000000000000000000362351342656743400210570ustar00rootroot00000000000000module 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: Gets or Sets the Hash proxy options. # attr_reader :proxy # 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) options = ConnectionOptions.from(options) if url.is_a?(Hash) || url.is_a?(ConnectionOptions) options = options.merge(url) url = options.url 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 @manual_proxy = !!options.proxy @proxy = options.proxy ? ProxyOptions.from(options.proxy) : proxy_from_env(url) @temp_proxy = @proxy 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::Request 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::Request 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? warn 'Warning: use of proxy(new_value) to set connection proxy have been DEPRECATED and will be removed in Faraday 1.0' @manual_proxy = true @proxy = ProxyOptions.from(arg) end # Public: Sets the Hash proxy options. def proxy=(new_value) @manual_proxy = true @proxy = new_value ? ProxyOptions.from(new_value) : nil 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 the 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 request body that will eventually be converted to a string. # 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 # Resets temp_proxy @temp_proxy = proxy_for_request(url) request = build_request(method) do |req| req.options = req.options.merge(:proxy => @temp_proxy) 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 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 def proxy_from_env(url) return if Faraday.ignore_env_proxy uri = nil if URI.parse('').respond_to?(:find_proxy) case url when String uri = Utils.URI(url) uri = URI.parse("#{uri.scheme}://#{uri.hostname}").find_proxy when URI uri = url.find_proxy when nil uri = find_default_proxy end else warn 'no_proxy is unsupported' if ENV['no_proxy'] || ENV['NO_PROXY'] uri = find_default_proxy end ProxyOptions.from(uri) if uri end def find_default_proxy uri = ENV['http_proxy'] if uri && !uri.empty? uri = 'http://' + uri if uri !~ /^http/i uri end end def proxy_for_request(url) return self.proxy if @manual_proxy if url && Utils.URI(url).absolute? proxy_from_env(url) else self.proxy end end end end ruby-faraday-0.15.4/lib/faraday/error.rb000066400000000000000000000027011342656743400200400ustar00rootroot00000000000000module Faraday class Error < StandardError; end class ClientError < Error attr_reader :response, :wrapped_exception 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 inner = '' if @wrapped_exception inner << " wrapped=#{@wrapped_exception.inspect}" end if @response inner << " response=#{@response.inspect}" end if inner.empty? inner << " #{super}" end %(#<#{self.class}#{inner}>) 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 class RetriableResponse < ClientError; end [:ClientError, :ConnectionFailed, :ResourceNotFound, :ParsingError, :TimeoutError, :SSLError, :RetriableResponse].each do |const| Error.const_set(const, Faraday.const_get(const)) end end ruby-faraday-0.15.4/lib/faraday/middleware.rb000066400000000000000000000013651342656743400210310ustar00rootroot00000000000000module 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 ruby-faraday-0.15.4/lib/faraday/options.rb000066400000000000000000000200621342656743400204020ustar00rootroot00000000000000module 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| sub_options = self.class.options_for(key) if sub_options new_value = sub_options.from(value) if value elsif value.is_a?(Hash) new_value = value.dup else new_value = value end self.send("#{key}=", new_value) unless new_value.nil? end self end # Public def delete(key) value = send(key) send("#{key}=", nil) value end # Public def clear members.each { |member| delete(member) } end # Public def merge!(other) other.each do |key, other_value| self_value = self.send(key) sub_options = self.class.options_for(key) new_value = (self_value && sub_options && other_value) ? self_value.merge(other_value) : other_value self.send("#{key}=", new_value) unless new_value.nil? end self end # Public def merge(other) dup.merge!(other) end # Public def deep_dup self.class.from(self) 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, :write_timeout, :boundary, :oauth, :context) 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, :min_version, :max_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 && uri.user && Utils.unescape(uri.user) } memoized(:password) { uri && 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, :reason_phrase) 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 ruby-faraday-0.15.4/lib/faraday/parameters.rb000066400000000000000000000132531342656743400210560ustar00rootroot00000000000000require "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) new_parent = "#{parent}%5B%5D" return new_parent if value.empty? buffer = "" value.each_with_index do |val, i| 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 ruby-faraday-0.15.4/lib/faraday/rack_builder.rb000066400000000000000000000155041342656743400213420ustar00rootroot00000000000000module 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 warn_middleware_after_adapter if adapter_set? @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) warn_middleware_after_adapter if inserting_after_adapter?(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) warn 'WARNING: No adapter was configured for this request' unless adapter_set? 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 warn_middleware_after_adapter warn "WARNING: Unexpected middleware set after the adapter. " \ "This won't be supported from Faraday 1.0." end def adapter_set? @handlers.any? { |handler| is_adapter?(handler) } end def inserting_after_adapter?(index) adapter_index = @handlers.find_index { |handler| is_adapter?(handler) } return false if adapter_index.nil? index > adapter_index end def is_adapter?(handler) handler.klass.ancestors.include? Faraday::Adapter 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 ruby-faraday-0.15.4/lib/faraday/request.rb000066400000000000000000000067711342656743400204120ustar00rootroot00000000000000module 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 anchor_index = path.index('#') path = path.slice(0, anchor_index) unless anchor_index.nil? 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 def marshal_dump { :method => method, :body => body, :headers => headers, :path => path, :params => params, :options => options } end def marshal_load(serialised) self.method = serialised[:method] self.body = serialised[:body] self.headers = serialised[:headers] self.path = serialised[:path] self.params = serialised[:params] self.options = serialised[:options] 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 ruby-faraday-0.15.4/lib/faraday/request/000077500000000000000000000000001342656743400200525ustar00rootroot00000000000000ruby-faraday-0.15.4/lib/faraday/request/authorization.rb000066400000000000000000000016341342656743400233030ustar00rootroot00000000000000module 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) comma = ", " 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 ruby-faraday-0.15.4/lib/faraday/request/basic_authentication.rb000066400000000000000000000004371342656743400245630ustar00rootroot00000000000000require '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 ruby-faraday-0.15.4/lib/faraday/request/instrumentation.rb000066400000000000000000000020101342656743400236330ustar00rootroot00000000000000module 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 ruby-faraday-0.15.4/lib/faraday/request/multipart.rb000066400000000000000000000041041342656743400224170ustar00rootroot00000000000000require File.expand_path("../url_encoded", __FILE__) require 'securerandom' module Faraday class Request::Multipart < Request::UrlEncoded self.mime_type = 'multipart/form-data'.freeze DEFAULT_BOUNDARY_PREFIX = "-----------RubyMultipartPost".freeze unless defined? DEFAULT_BOUNDARY_PREFIX def call(env) match_content_type(env) do |params| env.request.boundary ||= unique_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 unique_boundary "#{DEFAULT_BOUNDARY_PREFIX}-#{SecureRandom.hex}" 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 ruby-faraday-0.15.4/lib/faraday/request/retry.rb000066400000000000000000000163641342656743400215560ustar00rootroot00000000000000module 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 DEFAULT_EXCEPTIONS = [Errno::ETIMEDOUT, 'Timeout::Error', Error::TimeoutError, Faraday::Error::RetriableResponse].freeze IDEMPOTENT_METHODS = [:delete, :get, :head, :options, :put] class Options < Faraday::Options.new(:max, :interval, :max_interval, :interval_randomness, :backoff_factor, :exceptions, :methods, :retry_if, :retry_block, :retry_statuses) DEFAULT_CHECK = lambda { |env,exception| false } def self.from(value) if Integer === 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] ||= DEFAULT_EXCEPTIONS) end def methods Array(self[:methods] ||= IDEMPOTENT_METHODS) end def retry_if self[:retry_if] ||= DEFAULT_CHECK end def retry_block self[:retry_block] ||= Proc.new {} end def retry_statuses Array(self[:retry_statuses] ||= []) 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, Faraday::Error::RetriableResponse]) # 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) # retry_block - block that is executed after every retry. Request environment, middleware options, # current number of retries and the exception is passed to the block as parameters. def initialize(app, options = nil) super(app) @options = Options.from(options) @errmatch = build_exception_matcher(@options.exceptions) end def calculate_sleep_amount(retries, env) retry_after = calculate_retry_after(env) retry_interval = calculate_retry_interval(retries) return if retry_after && retry_after > @options.max_interval retry_after && retry_after >= retry_interval ? retry_after : retry_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).tap do |resp| raise Faraday::Error::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status) end rescue @errmatch => exception if retries > 0 && retry_request?(env, exception) retries -= 1 rewind_files(request_body) @options.retry_block.call(env, @options, retries, exception) if (sleep_amount = calculate_sleep_amount(retries + 1, env)) sleep sleep_amount retry end end if exception.is_a?(Faraday::Error::RetriableResponse) exception.response else raise end 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 def rewind_files(body) return unless body.is_a?(Hash) body.each do |_, value| if value.is_a? UploadIO value.rewind end end end # MDN spec for Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After def calculate_retry_after(env) response_headers = env[:response_headers] return unless response_headers retry_after_value = env[:response_headers]["Retry-After"] # Try to parse date from the header value begin datetime = DateTime.rfc2822(retry_after_value) datetime.to_time - Time.now.utc rescue ArgumentError retry_after_value.to_f end end def calculate_retry_interval(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 end end ruby-faraday-0.15.4/lib/faraday/request/token_authentication.rb000066400000000000000000000005201342656743400246130ustar00rootroot00000000000000module 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 ruby-faraday-0.15.4/lib/faraday/request/url_encoded.rb000066400000000000000000000017111342656743400226620ustar00rootroot00000000000000module 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 ruby-faraday-0.15.4/lib/faraday/response.rb000066400000000000000000000043411342656743400205470ustar00rootroot00000000000000require '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 reason_phrase finished? ? env.reason_phrase : 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 ruby-faraday-0.15.4/lib/faraday/response/000077500000000000000000000000001342656743400202205ustar00rootroot00000000000000ruby-faraday-0.15.4/lib/faraday/response/logger.rb000066400000000000000000000040631342656743400220270ustar00rootroot00000000000000require 'forwardable' module Faraday class Response::Logger < Response::Middleware extend Forwardable DEFAULT_OPTIONS = { :headers => true, :bodies => false } def initialize(app, logger = nil, options = {}) super(app) @logger = logger || begin require 'logger' ::Logger.new($stdout) end @filter = [] @options = DEFAULT_OPTIONS.merge(options) yield self if block_given? end def_delegators :@logger, :debug, :info, :warn, :error, :fatal def call(env) info('request') { "#{env.method.upcase} #{apply_filters(env.url.to_s)}" } debug('request') { apply_filters( dump_headers env.request_headers ) } if log_headers?(:request) debug('request') { apply_filters( dump_body(env[:body]) ) } if env[:body] && log_body?(:request) super end def on_complete(env) info('response') { "Status #{env.status.to_s}" } debug('response') { apply_filters( dump_headers env.response_headers ) } if log_headers?(:response) debug('response') { apply_filters( dump_body env[:body] ) } if env[:body] && log_body?(:response) end def filter(filter_word, filter_replacement) @filter.push([ filter_word, filter_replacement ]) 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_headers?(type) case @options[:headers] when Hash then @options[:headers][type] else @options[:headers] end end def log_body?(type) case @options[:bodies] when Hash then @options[:bodies][type] else @options[:bodies] end end def apply_filters(output) @filter.each do |pattern, replacement| output = output.to_s.gsub(pattern, replacement) end output end end end ruby-faraday-0.15.4/lib/faraday/response/raise_error.rb000066400000000000000000000012201342656743400230540ustar00rootroot00000000000000module 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 ruby-faraday-0.15.4/lib/faraday/upload_io.rb000066400000000000000000000026001342656743400206600ustar00rootroot00000000000000begin 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 ruby-faraday-0.15.4/lib/faraday/utils.rb000066400000000000000000000171241342656743400200540ustar00rootroot00000000000000require 'thread' module Faraday module Utils extend self # Adapted from Rack::Utils::HeaderHash class Headers < ::Hash def self.from(value) new(value) end def self.allocate new_self = super new_self.initialize_names new_self end def initialize(hash = nil) super() @names = {} self.update(hash || {}) end def initialize_names @names = {} 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? headers = header_string.split(/\r\n/) # Find the last set of response headers. start_index = headers.rindex { |x| x.match(/^HTTP\//) } || 0 last_response = headers.slice(start_index, headers.size) last_response. 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 ruby-faraday-0.15.4/script/000077500000000000000000000000001342656743400155115ustar00rootroot00000000000000ruby-faraday-0.15.4/script/bootstrap000077500000000000000000000005301342656743400174520ustar00rootroot00000000000000#!/bin/bash set -e if ! bundle config build.eventmachine | grep -q 'cppflags='; then if openssl_dir="$(brew --prefix openssl 2>/dev/null)"; then bundle config --local build.eventmachine \ --with-cppflags="-I${openssl_dir}/include" \ --with-ldflags="-L${openssl_dir}/lib" fi fi bundle install --path vendor/bundle --jobs 4 ruby-faraday-0.15.4/script/console000077500000000000000000000002611342656743400171000ustar00rootroot00000000000000#!/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%.*}" "$@" ruby-faraday-0.15.4/script/generate_certs000077500000000000000000000025131342656743400204320ustar00rootroot00000000000000#!/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' ruby-faraday-0.15.4/script/package000077500000000000000000000002301342656743400170250ustar00rootroot00000000000000#!/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 ruby-faraday-0.15.4/script/proxy-server000077500000000000000000000021551342656743400201270ustar00rootroot00000000000000#!/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 ruby-faraday-0.15.4/script/release000077500000000000000000000005531342656743400170620ustar00rootroot00000000000000#!/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 ruby-faraday-0.15.4/script/server000077500000000000000000000016171342656743400167520ustar00rootroot00000000000000#!/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 ruby-faraday-0.15.4/script/test000077500000000000000000000103141342656743400164150ustar00rootroot00000000000000#!/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 patron # # # 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 ruby-faraday-0.15.4/test/000077500000000000000000000000001342656743400151645ustar00rootroot00000000000000ruby-faraday-0.15.4/test/adapters/000077500000000000000000000000001342656743400167675ustar00rootroot00000000000000ruby-faraday-0.15.4/test/adapters/default_test.rb000066400000000000000000000004511342656743400217770ustar00rootroot00000000000000require 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 ruby-faraday-0.15.4/test/adapters/em_http_test.rb000066400000000000000000000015311342656743400220130ustar00rootroot00000000000000require 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 def test_custom_adapter_config url = URI('https://example.com:1234') adapter = Faraday::Adapter::EMHttp.new nil, inactivity_timeout: 20 req = adapter.create_request(url: url, request: {}) assert_equal 20, req.connopts.inactivity_timeout end end end ruby-faraday-0.15.4/test/adapters/em_synchrony_test.rb000066400000000000000000000015441342656743400230740ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class EMSynchronyTest < Faraday::TestCase def adapter() :em_synchrony end unless jruby? 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 end def test_custom_adapter_config url = URI('https://example.com:1234') adapter = Faraday::Adapter::EMSynchrony.new nil, inactivity_timeout: 20 req = adapter.create_request(url: url, request: {}) assert_equal 20, req.connopts.inactivity_timeout end end end ruby-faraday-0.15.4/test/adapters/excon_test.rb000066400000000000000000000015411342656743400214700ustar00rootroot00000000000000require 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 def test_custom_adapter_config url = URI('https://example.com:1234') adapter = Faraday::Adapter::Excon.new nil, debug_request: true conn = adapter.create_connection({url: url}, {}) assert_equal true, conn.data[:debug_request] end end end ruby-faraday-0.15.4/test/adapters/httpclient_test.rb000066400000000000000000000016521342656743400225350ustar00rootroot00000000000000require 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 def test_custom_adapter_config adapter = Faraday::Adapter::HTTPClient.new do |client| client.keep_alive_timeout = 20 client.ssl_config.timeout = 25 end client = adapter.client adapter.configure_client assert_equal 20, client.keep_alive_timeout assert_equal 25, client.ssl_config.timeout end end end end ruby-faraday-0.15.4/test/adapters/integration.rb000066400000000000000000000203501342656743400216370ustar00rootroot00000000000000require '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." warn "See CONTRIBUTING for usage." @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_GET_reason_phrase response = get('echo') assert_equal "OK", response.reason_phrase 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 = {}, &optional_connection_config_blk) 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, &optional_connection_config_blk 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 ruby-faraday-0.15.4/test/adapters/logger_test.rb000066400000000000000000000113541342656743400216360ustar00rootroot00000000000000require 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 do | logger | logger.filter(/(soylent green is) (.+)/,'\1 tasty') logger.filter(/(api_key:).*"(.+)."/,'\1[API_KEY]') logger.filter(/(password)=(.+)/,'\1=[HIDDEN]') end 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] } stubs.get('/filtered_body') { [200, {'Content-Type' => 'text/html'}, 'soylent green is people'] } stubs.get('/filtered_headers') { [200, {'Content-Type' => 'text/html'}, 'headers response'] } stubs.get('/filtered_params') { [200, {'Content-Type' => 'text/html'}, 'params response'] } stubs.get('/filtered_url') { [200, {'Content-Type' => 'text/html'}, 'url response'] } end end end def setup @io = StringIO.new @logger = Logger.new(@io) @logger.level = Logger::DEBUG @conn = conn(@logger) end def test_still_returns_output resp = @conn.get '/hello', nil, :accept => 'text/html' assert_equal 'hello', resp.body end def test_logs_method_and_url @conn.get '/hello', nil, :accept => 'text/html' assert_match 'request: GET http:/hello', @io.string end def test_logs_status_code @conn.get '/hello', nil, :accept => 'text/html' assert_match 'response: Status 200', @io.string end def test_logs_request_headers_by_default @conn.get '/hello', nil, :accept => 'text/html' assert_match %(Accept: "text/html), @io.string end def test_logs_response_headers_by_default @conn.get '/hello', nil, :accept => 'text/html' assert_match %(Content-Type: "text/html), @io.string end def test_does_not_log_request_headers_if_option_is_false app = conn(@logger, :headers => { :request => false }) app.get '/hello', nil, :accept => 'text/html' refute_match %(Accept: "text/html), @io.string end def test_does_log_response_headers_if_option_is_false app = conn(@logger, :headers => { :response => false }) app.get '/hello', nil, :accept => 'text/html' refute_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 def test_logs_filter_body app = conn(@logger, :bodies => true) app.get '/filtered_body', nil, :accept => 'text/html' assert_match %(soylent green is), @io.string assert_match %(tasty), @io.string refute_match %(people), @io.string end def test_logs_filter_headers app = conn(@logger) app.headers = {'api_key' => 'ABC123'} app.get '/filtered_headers', nil, :accept => 'text/html' assert_match %(api_key:), @io.string assert_match %([API_KEY]), @io.string refute_match %(ABC123), @io.string end def test_logs_filter_url app = conn(@logger) app.get '/filtered_url?password=hunter2', nil, :accept => 'text/html' assert_match %(password=[HIDDEN]), @io.string refute_match %(hunter2), @io.string end end end ruby-faraday-0.15.4/test/adapters/net_http_persistent_test.rb000066400000000000000000000071271342656743400244670ustar00rootroot00000000000000require 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 if Net::HTTP::Persistent.instance_method(:initialize).parameters.first == [:key, :name] Net::HTTP::Persistent.new(name: 'Faraday').reconnect_ssl else Net::HTTP::Persistent.new('Faraday').ssl_cleanup(4) end end end if ssl_mode? def test_reuses_tcp_sockets # Ensure that requests are not reused from previous tests Thread.current.keys .select { |key| key.to_s =~ /\Anet_http_persistent_Faraday_/ } .each { |key| Thread.current[key] = nil } sockets = [] tcp_socket_open_wrapped = Proc.new do |*args, &block| socket = TCPSocket.__minitest_stub__open(*args, &block) sockets << socket socket end TCPSocket.stub :open, tcp_socket_open_wrapped do conn = create_connection conn.post("/echo", :foo => "bar") conn.post("/echo", :foo => "baz") end assert_equal 1, sockets.count end def test_does_not_reuse_tcp_sockets_when_proxy_changes # Ensure that requests are not reused from previous tests Thread.current.keys .select { |key| key.to_s =~ /\Anet_http_persistent_Faraday_/ } .each { |key| Thread.current[key] = nil } sockets = [] tcp_socket_open_wrapped = Proc.new do |*args, &block| socket = TCPSocket.__minitest_stub__open(*args, &block) sockets << socket socket end TCPSocket.stub :open, tcp_socket_open_wrapped do conn = create_connection conn.post("/echo", :foo => "bar") conn.proxy = URI(ENV["LIVE_PROXY"]) conn.post("/echo", :foo => "bar") end assert_equal 2, sockets.count end end def test_without_custom_connection_config url = URI('https://example.com:1234') adapter = Faraday::Adapter::NetHttpPersistent.new http = adapter.send(:net_http_connection, :url => url, :request => {}) # `pool` is only present in net_http_persistent >= 3.0 assert http.pool.size != nil if http.respond_to?(:pool) end def test_custom_connection_config url = URI('https://example.com:1234') adapter = Faraday::Adapter::NetHttpPersistent.new(nil, {pool_size: 5}) http = adapter.send(:net_http_connection, :url => url, :request => {}) # `pool` is only present in net_http_persistent >= 3.0 assert_equal 5, http.pool.size if http.respond_to?(:pool) end def test_custom_adapter_config url = URI('https://example.com:1234') adapter = Faraday::Adapter::NetHttpPersistent.new do |http| http.idle_timeout = 123 end http = adapter.send(:net_http_connection, :url => url, :request => {}) adapter.send(:configure_request, http, {}) assert_equal 123, http.idle_timeout end def test_max_retries url = URI('http://example.com') adapter = Faraday::Adapter::NetHttpPersistent.new http = adapter.send(:net_http_connection, :url => url, :request => {}) adapter.send(:configure_request, http, {}) # `max_retries=` is only present in Ruby 2.5 assert_equal 0, http.max_retries if http.respond_to?(:max_retries=) end end end ruby-faraday-0.15.4/test/adapters/net_http_test.rb000066400000000000000000000041351342656743400222030ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) require 'ostruct' require 'uri' module Adapters class NetHttpTest < Faraday::TestCase def adapter() :net_http end behaviors = [:NonParallel, :Compression] 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.send(: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.send(: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.send(:net_http_connection, :url => url, :request => {}) assert_equal 1234, http.port end def test_custom_adapter_config url = URI('https://example.com:1234') adapter = Faraday::Adapter::NetHttp.new do |http| http.continue_timeout = 123 end http = adapter.send(:net_http_connection, :url => url, :request => {}) adapter.send(:configure_request, http, {}) assert_equal 123, http.continue_timeout end def test_no_retries url = URI('http://example.com') adapter = Faraday::Adapter::NetHttp.new http = adapter.send(:net_http_connection, :url => url, :request => {}) adapter.send(:configure_request, http, {}) # `max_retries=` is only present in Ruby 2.5 assert_equal 0, http.max_retries if http.respond_to?(:max_retries=) end def test_write_timeout url = URI('http://example.com') adapter = Faraday::Adapter::NetHttp.new http = adapter.send(:net_http_connection, :url => url, :request => {}) adapter.send(:configure_request, http, { write_timeout: 10 }) assert_equal 10, http.write_timeout if http.respond_to?(:write_timeout=) end end end ruby-faraday-0.15.4/test/adapters/patron_test.rb000066400000000000000000000021121342656743400216520ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class Patron < Faraday::TestCase def adapter() :patron end unless jruby? 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 def test_custom_adapter_config conn = create_connection do |session| assert_kind_of ::Patron::Session, session session.max_redirects = 10 throw :config_block_called end assert_throws(:config_block_called) do conn.get 'http://8.8.8.8:88' end end def test_connection_timeout conn = create_connection(:request => {:timeout => 10, :open_timeout => 1}) assert_raises Faraday::Error::ConnectionFailed do conn.get 'http://8.8.8.8:88' end end end end end ruby-faraday-0.15.4/test/adapters/rack_test.rb000066400000000000000000000020051342656743400212700ustar00rootroot00000000000000require 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 # Rack::MockResponse doesn't provide any way to access the reason phrase, # so override the shared test from Common. def test_GET_reason_phrase response = get('echo') assert_nil response.reason_phrase end # 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 ruby-faraday-0.15.4/test/adapters/test_middleware_test.rb000066400000000000000000000122041342656743400235260ustar00rootroot00000000000000require File.expand_path('../../helper', __FILE__) module Adapters class TestMiddleware < Faraday::TestCase Stubs = Faraday::Adapter.lookup_middleware(:test)::Stubs def setup @stubs = Stubs.new do |stub| stub.get('/hello') do [200, {'Content-Type' => 'text/html'}, 'hello'] end stub.get('/method-echo') do |env| [200, {'Content-Type' => 'text/html'}, env[:method].to_s] end stub.get(/\A\/resources\/\d+(?:\?|\z)/) do [200, {'Content-Type' => 'text/html'}, 'show'] end stub.get(/\A\/resources\/(specified)\z/) do |env, meta| [200, {'Content-Type' => 'text/html'}, "show #{meta[:match_data][1]}"] end stub.get('http://domain.test/hello') do [200, {'Content-Type' => 'text/html'}, 'domain: hello'] end stub.get('http://wrong.test/hello') do [200, {'Content-Type' => 'text/html'}, 'wrong: hello'] end stub.get('http://wrong.test/bait') do [404, {'Content-Type' => 'text/html'}] end end @conn = Faraday.new do |builder| builder.adapter :test, @stubs end @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_with_host_points_to_the_right_stub assert_equal 'domain: hello', @conn.get('http://domain.test/hello').body end def test_middleware_can_be_called_several_times assert_equal 'hello', @conn.get('/hello').body end def test_middleware_can_handle_regular_expression_path assert_equal 'show', @conn.get('/resources/1').body end def test_middleware_can_handle_single_parameter_block assert_equal 'get', @conn.get('/method-echo').body end def test_middleware_can_handle_regular_expression_path_with_captured_result assert_equal 'show specified', @conn.get('/resources/specified').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_specified_host assert_raises Stubs::NotFound do @conn.get('http://domain.test/bait') 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 ruby-faraday-0.15.4/test/adapters/typhoeus_test.rb000066400000000000000000000022721342656743400222360ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class TyphoeusTest < Faraday::TestCase def adapter() :typhoeus end Integration.apply(self, :Parallel) do # inconsistent outcomes ranging from successful response to connection error undef :test_proxy_auth_fail if ssl_mode? # Typhoeus adapter not supporting Faraday::SSLError undef :test_GET_ssl_fails_with_bad_cert 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 # Typhoeus::Response doesn't provide an easy way to access the reason phrase, # so override the shared test from Common. def test_GET_reason_phrase response = get('echo') assert_nil response.reason_phrase end end def test_custom_adapter_config adapter = Faraday::Adapter::Typhoeus.new(nil, { :forbid_reuse => true, :maxredirs => 1 }) request = adapter.method(:typhoeus_request).call({}) assert_equal true, request.options[:forbid_reuse] assert_equal 1, request.options[:maxredirs] end end end ruby-faraday-0.15.4/test/authentication_middleware_test.rb000066400000000000000000000043471342656743400237740ustar00rootroot00000000000000require 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 ruby-faraday-0.15.4/test/composite_read_io_test.rb000066400000000000000000000056471342656743400222500ustar00rootroot00000000000000require 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_nil io.read(3) assert_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_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_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_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 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 def bin(str) str.force_encoding("BINARY") if str.respond_to?(:force_encoding) str end end ruby-faraday-0.15.4/test/connection_test.rb000066400000000000000000000556331342656743400207230ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class TestConnection < Faraday::TestCase def teardown Faraday.default_connection_options = nil end def with_test_conn old_conn = Faraday.default_connection Faraday.default_connection = Faraday::Connection.new do |builder| builder.adapter :test do |stub| stub.get('/') do |_| [200, nil, nil] end end end begin yield ensure Faraday.default_connection = old_conn end end def with_env_proxy_disabled Faraday.ignore_env_proxy = true begin yield ensure Faraday.ignore_env_proxy = false end end def with_env(new_env) old_env = {} new_env.each do |key, value| old_env[key] = ENV.fetch(key, false) ENV[key] = value end begin yield ensure old_env.each do |key, value| if value == false ENV.delete key else ENV[key] = value end 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(nil, '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_nil conn.proxy end end def test_no_proxy_from_blank_env with_env 'http_proxy' => '' do conn = Faraday::Connection.new assert_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_dynamic_proxy with_test_conn do with_env 'http_proxy' => 'http://duncan.proxy.com:80' do Faraday.get('http://google.co.uk') assert_equal 'duncan.proxy.com', Faraday.default_connection.instance_variable_get('@temp_proxy').host end Faraday.get('http://google.co.uk') assert_nil Faraday.default_connection.instance_variable_get('@temp_proxy') end end def test_ignore_env_proxy with_env_proxy_disabled do with_env 'http_proxy' => 'http://duncan.proxy.com:80' do conn = Faraday::Connection.new(proxy: nil) assert_nil conn.proxy end end end if URI.parse('').respond_to?(:find_proxy) def test_proxy_allowed_when_url_in_no_proxy_list with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do conn = Faraday::Connection.new('http://example.com') assert_nil conn.proxy end end def test_proxy_allowed_when_prefixed_url_is_not_in_no_proxy_list with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do conn = Faraday::Connection.new('http://prefixedexample.com') assert_equal 'proxy.com', conn.proxy.host end end def test_proxy_allowed_when_subdomain_url_is_in_no_proxy_list with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do conn = Faraday::Connection.new('http://subdomain.example.com') assert_nil conn.proxy end end def test_proxy_allowed_when_url_not_in_no_proxy_list with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example2.com' do conn = Faraday::Connection.new('http://example.com') assert_equal 'proxy.com', conn.proxy.host end end def test_proxy_allowed_when_ip_address_is_not_in_no_proxy_list_but_url_is with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'localhost' do conn = Faraday::Connection.new('http://127.0.0.1') assert_nil conn.proxy end end def test_proxy_allowed_when_url_is_not_in_no_proxy_list_but_ip_address_is with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => '127.0.0.1' do conn = Faraday::Connection.new('http://localhost') assert_nil conn.proxy end end def test_proxy_allowed_in_multi_element_no_proxy_list with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example0.com,example.com,example1.com' do assert_nil Faraday::Connection.new('http://example0.com').proxy assert_nil Faraday::Connection.new('http://example.com').proxy assert_nil Faraday::Connection.new('http://example1.com').proxy assert_equal 'proxy.com', Faraday::Connection.new('http://example2.com').proxy.host end end def test_dynamic_no_proxy with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'google.co.uk' do conn = Faraday.new assert_equal 'proxy.com', conn.instance_variable_get('@temp_proxy').host conn.get('https://google.co.uk') assert_nil conn.instance_variable_get('@temp_proxy') end end def test_issue with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'google.co.uk' do conn = Faraday.new conn.proxy = 'http://proxy2.com' assert_equal true, conn.instance_variable_get('@manual_proxy') assert_equal 'proxy2.com', conn.proxy_for_request('https://google.co.uk').host end 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 test_respond_to assert Faraday.respond_to?(:get) assert Faraday.respond_to?(:post) end def test_default_connection_options Faraday.default_connection_options.request.timeout = 10 conn = Faraday.new 'http://sushi.com/foo' assert_equal 10, conn.options.timeout end def test_default_connection_options_without_url Faraday.default_connection_options.request.timeout = 10 conn = Faraday.new :url => 'http://sushi.com/foo' assert_equal 10, conn.options.timeout end def test_default_connection_options_as_hash Faraday.default_connection_options = { request: { timeout: 10 } } conn = Faraday.new 'http://sushi.com/foo' assert_equal 10, conn.options.timeout end def test_default_connection_options_as_hash_without_url Faraday.default_connection_options = { request: { timeout: 10 } } conn = Faraday.new :url => 'http://sushi.com/foo' assert_equal 10, conn.options.timeout end def test_default_connection_options_as_hash_with_instance_connection_options Faraday.default_connection_options = { request: { timeout: 10 } } conn = Faraday.new 'http://sushi.com/foo', request: { open_timeout: 1 } assert_equal 1, conn.options.open_timeout assert_equal 10, conn.options.timeout end def test_default_connection_options_persist_with_an_instance_overriding Faraday.default_connection_options.request.timeout = 10 conn = Faraday.new 'http://nigiri.com/bar' conn.options.timeout = 1 assert_equal 10, Faraday.default_connection_options.request.timeout other = Faraday.new :url => 'https://sushi.com/foo' other.options.timeout = 1 assert_equal 10, Faraday.default_connection_options.request.timeout end def test_default_connection_uses_default_connection_options Faraday.default_connection_options.request.timeout = 10 default_conn = Faraday.default_connection assert_equal 10, default_conn.options.timeout 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 ruby-faraday-0.15.4/test/env_test.rb000066400000000000000000000175461342656743400173550ustar00rootroot00000000000000require 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_uri_with_anchor env = make_env do |req| req.url 'foo.json?b=2&a=1#qqq' end assert_equal 'http://sushi.com/api/foo.json?a=1&b=2', 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' req.options.context = { foo: 'foo', bar: 'bar' } end assert_equal 10, env.request.timeout assert_equal 5, env.request.open_timeout assert_equal 'boo', env.request.boundary assert_equal env.request.context, { foo: 'foo', bar: 'bar' } 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_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_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 @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_marshal_request @request = Faraday::Request.create(:post) do |request| request.options = Faraday::RequestOptions.new request.params = Faraday::Utils::ParamsHash.new({ 'a' => 'c' }) request.headers = { 'b' => 'd' } request.body = 'hello, world!' request.url 'http://localhost/foo' end loaded = Marshal.load(Marshal.dump(@request)) assert_equal @request, loaded 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 ruby-faraday-0.15.4/test/helper.rb000066400000000000000000000026151342656743400167740ustar00rootroot00000000000000require 'simplecov' require 'coveralls' SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] SimpleCov.start do add_filter '/bundle/' add_filter '/test/' minimum_coverage(87) 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 ruby-faraday-0.15.4/test/live_server.rb000066400000000000000000000024401342656743400200360ustar00rootroot00000000000000require '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 ruby-faraday-0.15.4/test/middleware/000077500000000000000000000000001342656743400173015ustar00rootroot00000000000000ruby-faraday-0.15.4/test/middleware/instrumentation_test.rb000066400000000000000000000041131342656743400241270ustar00rootroot00000000000000require 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 ruby-faraday-0.15.4/test/middleware/retry_test.rb000066400000000000000000000227221342656743400220370ustar00rootroot00000000000000require 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 stub.send(method, '/throttled') do |env| @times_called += 1 @envs << env.dup params = env[:params] status = (params['status'] || 429).to_i headers = {} retry_after = params['retry_after'] headers['Retry-After'] = retry_after if retry_after [status, headers, ''] 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_calculate_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 calculate_sleep_amount(retries, env) 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.send(:calculate_retry_interval, 5), 0.1 assert_equal middleware.send(:calculate_retry_interval, 4), 0.2 assert_equal middleware.send(:calculate_retry_interval, 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.send(:calculate_retry_interval, 5), 1 assert_equal middleware.send(:calculate_retry_interval, 4), 2 assert_equal middleware.send(:calculate_retry_interval, 3), 3 assert_equal middleware.send(:calculate_retry_interval, 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.send(:calculate_retry_interval, 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.send(:calculate_retry_interval, 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.send(:calculate_retry_interval, 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 def test_should_rewind_files_on_retry io = StringIO.new("Test data") upload_io = Faraday::UploadIO.new(io, "application/octet/stream") rewound = 0 rewind = lambda { rewound += 1 } upload_io.stub :rewind, rewind do @explode = lambda {|n| raise Errno::ETIMEDOUT } check = lambda { |env,exception| true } assert_raises(Errno::ETIMEDOUT) { conn(:retry_if => check).post("/unstable", { :file => upload_io }) } end assert_equal 3, @times_called assert_equal 2, rewound end def test_should_retry_retriable_response params = { status: 429 } response = conn(:max => 1, :retry_statuses => 429).get("/throttled", params) assert_equal 2, @times_called assert_equal 429, response.status end def test_should_not_retry_non_retriable_response params = { status: 503 } conn(:max => 1, :retry_statuses => 429).get("/throttled", params) assert_equal 1, @times_called end def test_interval_if_retry_after_present started = Time.now params = { :retry_after => 0.5 } conn(:max => 1, :interval => 0.1, :retry_statuses => [429]).get("/throttled", params) assert Time.now - started > 0.5 end def test_should_ignore_retry_after_if_less_then_calculated started = Time.now params = { :retry_after => 0.1 } conn(:max => 1, :interval => 0.2, :retry_statuses => [429]).get("/throttled", params) assert Time.now - started > 0.2 end def test_interval_when_retry_after_is_timestamp started = Time.now params = { :retry_after => (Time.now.utc + 2).strftime('%a, %d %b %Y %H:%M:%S GMT') } conn(:max => 1, :interval => 0.1, :retry_statuses => [429]).get("/throttled", params) assert Time.now - started > 1 end def test_should_not_retry_when_retry_after_greater_then_max_interval params = { :retry_after => (Time.now.utc + 20).strftime('%a, %d %b %Y %H:%M:%S GMT') } conn(:max => 2, :interval => 0.1, :retry_statuses => [429], max_interval: 5).get("/throttled", params) assert_equal 1, @times_called end end end ruby-faraday-0.15.4/test/middleware_stack_test.rb000066400000000000000000000154041342656743400220560ustar00rootroot00000000000000require 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_handlers_stack Apple @builder.use Orange @builder.adapter :test, &test_adapter 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_handlers_stack Apple, Orange @builder.insert_before Apple, Banana @builder.adapter :test, &test_adapter assert_handlers %w[Banana Apple Orange] end def test_insert_after build_handlers_stack Apple, Orange @builder.insert_after Apple, Banana @builder.adapter :test, &test_adapter assert_handlers %w[Apple Banana Orange] end def test_swap_handlers build_handlers_stack Apple, Orange @builder.swap Apple, Banana @builder.adapter :test, &test_adapter 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? @builder.adapter :test, &test_adapter end end def build_handlers_stack(*handlers) @builder.build do |b| handlers.each { |handler| b.use(*handler) } end end def test_adapter Proc.new 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 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 class MiddlewareStackOrderTest < Faraday::TestCase def test_adding_response_middleware_after_adapter response_after_adapter = lambda do Faraday::RackBuilder.new do |b| b.adapter :test b.response :raise_error end end assert_output("", expected_middleware_warning, &response_after_adapter) end def test_adding_request_middleware_after_adapter request_after_adapter = lambda do Faraday::RackBuilder.new do |b| b.adapter :test b.request :multipart end end assert_output("", expected_middleware_warning, &request_after_adapter) end def test_adding_request_middleware_after_adapter_via_use use_after_adapter = lambda do Faraday::RackBuilder.new do |b| b.adapter :test b.use Faraday::Request::Multipart end end assert_output("", expected_middleware_warning, &use_after_adapter) end def test_adding_request_middleware_after_adapter_via_insert insert_after_adapter = lambda do Faraday::RackBuilder.new do |b| b.adapter :test b.insert(1, Faraday::Request::Multipart) end end assert_output("", expected_middleware_warning, &insert_after_adapter) end def test_adding_request_middleware_before_adapter_via_insert_no_warning builder = Faraday::RackBuilder.new do |b| b.adapter :test end insert_before_adapter = lambda do builder.insert(0, Faraday::Request::Multipart) end assert_output("", "", &insert_before_adapter) end private def expected_middleware_warning /Unexpected middleware set after the adapter/ end end ruby-faraday-0.15.4/test/multibyte.txt000066400000000000000000000000151342656743400177370ustar00rootroot00000000000000ファイル ruby-faraday-0.15.4/test/options_test.rb000066400000000000000000000200771342656743400202510ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class OptionsTest < Faraday::TestCase SubOptions = Class.new(Faraday::Options.new(:sub_a, :sub_b)) class ParentOptions < Faraday::Options.new(:a, :b, :c) options :c => SubOptions end def test_deep_merge sub_opts1 = SubOptions.from(sub_a: 3) sub_opts2 = SubOptions.from(sub_b: 4) opt1 = ParentOptions.from(a: 1, c: sub_opts1) opt2 = ParentOptions.from(b: 2, c: sub_opts2) merged = opt1.merge(opt2) expected_sub_opts = SubOptions.from(sub_a: 3, sub_b: 4) assert_equal merged, ParentOptions.from(a: 1, b: 2, c: expected_sub_opts) end def test_deep_merge_with_hash sub_opts1 = SubOptions.from(sub_a: 3) sub_opts2 = { sub_b: 4 } opt1 = ParentOptions.from(a: 1, c: sub_opts1) opt2 = { b: 2, c: sub_opts2 } merged = opt1.merge(opt2) expected_sub_opts = SubOptions.from(sub_a: 3, sub_b: 4) assert_equal merged, ParentOptions.from(a: 1, b: 2, c: expected_sub_opts) end def test_deep_merge_with_nil sub_opts = SubOptions.new(3, 4) options = ParentOptions.new(1, 2, sub_opts) assert_equal options.a, 1 assert_equal options.b, 2 assert_equal options.c.sub_a, 3 assert_equal options.c.sub_b, 4 options2 = ParentOptions.from(b: 5, c: nil) merged = options.merge(options2) assert_equal merged.b, 5 assert_equal merged.c, sub_opts end def test_deep_merge_with_sub_nil options = ParentOptions.from(a: 1) sub_opts = SubOptions.new(3, 4) options2 = ParentOptions.from(b: 2, c: sub_opts) assert_equal options.a, 1 assert_equal options2.b, 2 assert_equal options2.c.sub_a, 3 assert_equal options2.c.sub_b, 4 merged = options.merge(options2) assert_equal merged.c, sub_opts end def test_dup_is_shallow sub_opts = SubOptions.from(sub_a: 3) opts = ParentOptions.from(b: 1, c: sub_opts) duped = opts.dup duped.b = 2 duped.c.sub_a = 4 assert_equal opts.b, 1 assert_equal opts.c.sub_a, 4 end def test_deep_dup sub_opts = SubOptions.from(sub_a: 3) opts = ParentOptions.from(b: 1, c: sub_opts) duped = opts.deep_dup duped.b = 2 duped.c.sub_a = 4 assert_equal opts.b, 1 assert_equal opts.c.sub_a, 3 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_a = 1 assert !options.empty? options.delete(:sub_a) 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_a) options.sub_a = 1 assert options.key?(:sub_a) 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_a = 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_from_nil options = Faraday::ProxyOptions.from nil assert_kind_of Faraday::ProxyOptions, options 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_a 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_a => 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_a end def test_inheritance subclass = Class.new(ParentOptions) options = subclass.from(:c => {:sub_a => 'hello'}) assert_kind_of SubOptions, options.c assert_equal 'hello', options.c.sub_a 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 ruby-faraday-0.15.4/test/parameters_test.rb000066400000000000000000000121011342656743400207060ustar00rootroot00000000000000require 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 def test_encode_empty_string_array_value expected = 'baz=&foo%5Bbar%5D=' assert_equal expected, Faraday::NestedParamsEncoder.encode(foo: {bar: ''}, baz: '') end def test_encode_nil_array_value expected = 'baz&foo%5Bbar%5D' assert_equal expected, Faraday::NestedParamsEncoder.encode(foo: {bar: nil}, baz: nil) end def test_encode_empty_array_value expected = 'baz%5B%5D&foo%5Bbar%5D%5B%5D' Faraday::NestedParamsEncoder.encode(foo: { bar: [] }, baz: []) assert_equal expected, Faraday::NestedParamsEncoder.encode(foo: { bar: [] }, baz: []) end end ruby-faraday-0.15.4/test/request_middleware_test.rb000066400000000000000000000106431342656743400224410ustar00rootroot00000000000000# encoding: utf-8 require File.expand_path('../helper', __FILE__) Faraday::CompositeReadIO.class_eval { attr_reader :ios } class RequestMiddlewareTest < Faraday::TestCase def 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 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_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 response.headers['Content-Type'].start_with?( "multipart/form-data; boundary=%s" % Faraday::Request::Multipart::DEFAULT_BOUNDARY_PREFIX, ) 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 response.headers['Content-Type'].start_with?( "multipart/form-data; boundary=%s" % Faraday::Request::Multipart::DEFAULT_BOUNDARY_PREFIX, ) 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_unique_boundary payload = {:a => 1, :b =>[{:c => Faraday::UploadIO.new(__FILE__, 'text/x-ruby'), :d => 2}]} response1 = conn.post('/echo', payload) response2 = conn.post('/echo', payload) assert response1.headers['Content-Type'] != response2.headers['Content-Type'] end end ruby-faraday-0.15.4/test/response_middleware_test.rb000066400000000000000000000035321342656743400226060ustar00rootroot00000000000000require 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_nil @conn.get('no_content').body end def test_304 assert_nil @conn.get('not_modified').body end end ruby-faraday-0.15.4/test/strawberry.rb000066400000000000000000000001111342656743400177060ustar00rootroot00000000000000class MiddlewareStackTest::Strawberry < MiddlewareStackTest::Handler end ruby-faraday-0.15.4/test/utils_test.rb000066400000000000000000000047701342656743400177200ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class TestUtils < Faraday::TestCase # Headers parsing def test_headers "HTTP/1.x 500 OK\r\nContent-Type: text/html; charset=UTF-8\r\n" \ "HTTP/1.x 200 OK\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n" end def test_headers_parsing_for_aggregated_responses headers = Faraday::Utils::Headers.new() headers.parse(test_headers) result = headers.to_hash assert_equal result["Content-Type"], "application/json; charset=UTF-8" end # URI parsing 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 # YAML parsing def test_headers_yaml_roundtrip headers = Faraday::Utils::Headers.new('User-Agent' => 'safari', 'Content-type' => 'text/html') result = YAML.load(headers.to_yaml) assert result.include?('user-agent'), 'Unable to hydrate to a correct Headers' assert result.include?('content-type'), 'Unable to hydrate to a correct Headers' assert result['user-agent'] == 'safari', 'Unable to access rehydrated Headers' assert result['content-type'] == 'text/html', 'Unable to access rehydrated Headers' end end