pax_global_header00006660000000000000000000000064131406410200014501gustar00rootroot0000000000000052 comment=d72ba5ea88ed117140f38bbc852c10ff67948393 faraday_middleware-0.12.2/000077500000000000000000000000001314064102000153675ustar00rootroot00000000000000faraday_middleware-0.12.2/.gitignore000066400000000000000000000003401314064102000173540ustar00rootroot00000000000000# TextMate *.tmproj tmtags # emails *~ \#* .\#* # vim *.swp # General .ruby-version bin coverage rdoc doc log .yardoc tmp # Bundler *.gem .bundle Gemfile*.lock pkg *.gem vendor/bundle # Rubinius *.rbc # RubyMine .idea faraday_middleware-0.12.2/.rspec000066400000000000000000000000271314064102000165030ustar00rootroot00000000000000--color --order random faraday_middleware-0.12.2/.travis.yml000066400000000000000000000013251314064102000175010ustar00rootroot00000000000000sudo: false bundler_args: --without development language: ruby rvm: - 1.9.3 - 2.0 - 2.1 - 2.2 - 2.3.0 - 2.4.0 - jruby-head - rbx-2 gemfile: - Gemfile - Gemfile-0.9.rb matrix: exclude: - rvm: 2.4.0 gemfile: Gemfile-0.9.rb allow_failures: - rvm: jruby-head - rvm: rbx-2 fast_finish: true deploy: provider: rubygems api_key: secure: H5OPkiOcBFpI34nxIdGc/Rj6nNQKBUR0ccyKBubFroXcVDftpA+TVx8Vq+sjYaM1GuZI1F3OJPLHXmuyYsnJ3M3yltG2pYpJehULdpVU6yMu6eaQ8UAzA8wbmM1ANWEGQi1giulnxHw9M08qFzECpZvDFJWjtCg9lws64emqPLA= gem: faraday_middleware on: tags: true repo: lostisland/faraday_middleware rvm: 2.4.0 gemfile: Gemfile condition: "$TRAVIS_RUBY_VERSION == 2.4.0" faraday_middleware-0.12.2/CHANGELOG.md000066400000000000000000000002051314064102000171750ustar00rootroot00000000000000# Changelog ### 0.0.2 September 25, 2010 * Mashify now handles arrays of non-hashes ### 0.0.1 June 27, 2010 * MultiJSON * Mashifyfaraday_middleware-0.12.2/CONTRIBUTING.md000066400000000000000000000034251314064102000176240ustar00rootroot00000000000000## Contributing In the spirit of [free software][free-sw], **everyone** is encouraged to help improve this project. [free-sw]: http://www.fsf.org/licensing/essays/free-sw.html Here are some ways *you* can contribute: * by using alpha, beta, and prerelease versions * by reporting bugs * by suggesting new features * by writing or editing documentation * by writing specifications * by writing code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace) * by refactoring code * by fixing [issues][] * by reviewing patches [issues]: https://github.com/lostisland/faraday_middleware/issues ## Submitting an Issue We use the [GitHub issue tracker][issues] to track bugs and features. Before submitting a bug report or feature request, check to make sure it hasn't already been submitted. When submitting a bug report, please include a [Gist][] that includes a stack trace and any details that may be necessary to reproduce the bug, including your gem version, Ruby version, and operating system. Ideally, a bug report should include a pull request with failing specs. [gist]: https://gist.github.com/ ## Submitting a Pull Request 1. [Fork the repository.][fork] 2. [Create a topic branch.][branch] 3. Add specs for your unimplemented feature or bug fix. 4. Run `bundle exec rake spec`. If your specs pass, return to step 3. 5. Implement your feature or bug fix. 6. Run `COVERAGE=true bundle exec rake spec`. If your specs fail, return to step 5. 7. Run `open coverage/index.html`. If your changes are not completely covered by your tests, return to step 3. 8. Add, commit, and push your changes. 9. [Submit a pull request.][pr] [fork]: http://help.github.com/fork-a-repo/ [branch]: http://learn.github.com/p/branching.html [pr]: http://help.github.com/send-pull-requests/ faraday_middleware-0.12.2/Gemfile000066400000000000000000000011031314064102000166550ustar00rootroot00000000000000source 'https://rubygems.org' gem 'hashie', '>= 1.2' gem 'jruby-openssl', :platforms => :jruby gem 'json', '< 2' gem 'multi_xml', '>= 0.5.3' gem 'rack', '< 2' gem 'rack-cache', '>= 1.1', '< 1.3' gem 'rake', '>= 0.9', '< 11' gem 'rash_alt', '>= 0.4.3' gem 'simple_oauth', '>= 0.1', '< 0.3' gem 'safe_yaml' group :test do gem 'cane', '>= 2.2.2', :platforms => [:mri_19, :mri_20, :mri_21] gem 'parallel', '< 1.3.4', :platforms => [:mri_19, :mri_20, :mri_21] gem 'rspec', '>= 3' gem 'simplecov', '~> 0.12.0' gem 'webmock', '< 2' gem 'addressable', '< 2.4' end gemspec faraday_middleware-0.12.2/Gemfile-0.7.rb000066400000000000000000000001241314064102000175630ustar00rootroot00000000000000eval File.read(File.expand_path('../Gemfile', __FILE__)) gem 'faraday', '~> 0.7.4' faraday_middleware-0.12.2/Gemfile-0.8.rb000066400000000000000000000001241314064102000175640ustar00rootroot00000000000000eval File.read(File.expand_path('../Gemfile', __FILE__)) gem 'faraday', '~> 0.8.8' faraday_middleware-0.12.2/Gemfile-0.9.rb000066400000000000000000000001241314064102000175650ustar00rootroot00000000000000eval File.read(File.expand_path('../Gemfile', __FILE__)) gem 'faraday', '~> 0.9.2' faraday_middleware-0.12.2/LICENSE.md000066400000000000000000000020771314064102000170010ustar00rootroot00000000000000Copyright (c) 2011 Erik Michaels-Ober, Wynn Netherland, et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. faraday_middleware-0.12.2/README.md000066400000000000000000000030131314064102000166430ustar00rootroot00000000000000Faraday Middleware ================== A collection of useful [Faraday][] middleware. [See the documentation][docs]. gem install faraday_middleware Dependencies ------------ Some dependent libraries are needed only when using specific middleware: * FaradayMiddleware::EncodeJson & FaradayMiddleware::ParseJson: "json" for ruby older than 1.9 * FaradayMiddleware::ParseXml: "multi_xml" * FaradayMiddleware::OAuth: "simple_oauth" * FaradayMiddleware::Mashify: "hashie" * FaradayMiddleware::Rashify: "rash_alt" (Make sure to uninstall original rash gem to avoid conflict) * FaradayMiddleware::Instrumentation: "activesupport" Examples -------- ``` rb require 'faraday_middleware' ## in Faraday 0.8 or above: connection = Faraday.new 'http://example.com/api' do |conn| conn.request :oauth2, 'TOKEN' conn.request :json conn.response :xml, :content_type => /\bxml$/ conn.response :json, :content_type => /\bjson$/ conn.use :instrumentation conn.adapter Faraday.default_adapter end ## with Faraday 0.7: connection = Faraday.new 'http://example.com/api' do |builder| builder.use FaradayMiddleware::OAuth2, 'TOKEN' builder.use FaradayMiddleware::EncodeJson builder.use FaradayMiddleware::ParseXml, :content_type => /\bxml$/ builder.use FaradayMiddleware::ParseJson, :content_type => /\bjson$/ builder.use FaradayMiddleware::Instrumentation builder.adapter Faraday.default_adapter end ``` [faraday]: https://github.com/lostisland/faraday#readme [docs]: https://github.com/lostisland/faraday_middleware/wiki faraday_middleware-0.12.2/Rakefile000066400000000000000000000015141314064102000170350ustar00rootroot00000000000000ruby_19 = RUBY_VERSION > '1.9' ruby_mri = !defined?(RUBY_ENGINE) || 'ruby' == RUBY_ENGINE default_gemfile = ENV['BUNDLE_GEMFILE'] =~ /Gemfile$/ begin require 'cane/rake_task' rescue LoadError warn "warning: cane not available; skipping quality checks." else desc %(Check code quality metrics with Cane) Cane::RakeTask.new(:quality) do |cane| cane.abc_max = 15 cane.style_measure = 110 cane.max_violations = 0 cane.add_threshold 'coverage/covered_percent', :>=, 98.5 end end if ruby_19 && ruby_mri && default_gemfile task :default => [:enable_coverage, :spec] task :default => :quality if defined?(Cane) else task :default => [:spec] end require 'bundler' Bundler::GemHelper.install_tasks require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) task :enable_coverage do ENV['COVERAGE'] = 'yes' end faraday_middleware-0.12.2/faraday_middleware.gemspec000066400000000000000000000012341314064102000225400ustar00rootroot00000000000000# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'faraday_middleware/version' Gem::Specification.new do |spec| spec.name = 'faraday_middleware' spec.version = FaradayMiddleware::VERSION spec.summary = %q{Various middleware for Faraday} spec.authors = ["Erik Michaels-Ober", "Wynn Netherland"] spec.email = ['sferik@gmail.com', 'wynn.netherland@gmail.com'] spec.homepage = 'https://github.com/lostisland/faraday_middleware' spec.licenses = ['MIT'] spec.add_dependency 'faraday', ['>= 0.7.4', '< 1.0'] spec.files = `git ls-files -z lib LICENSE.md README.md`.split("\0") end faraday_middleware-0.12.2/lib/000077500000000000000000000000001314064102000161355ustar00rootroot00000000000000faraday_middleware-0.12.2/lib/faraday_middleware.rb000066400000000000000000000041761314064102000222760ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware autoload :OAuth, 'faraday_middleware/request/oauth' autoload :OAuth2, 'faraday_middleware/request/oauth2' autoload :EncodeJson, 'faraday_middleware/request/encode_json' autoload :MethodOverride, 'faraday_middleware/request/method_override' autoload :Mashify, 'faraday_middleware/response/mashify' autoload :Rashify, 'faraday_middleware/response/rashify' autoload :ParseJson, 'faraday_middleware/response/parse_json' autoload :ParseXml, 'faraday_middleware/response/parse_xml' autoload :ParseMarshal, 'faraday_middleware/response/parse_marshal' autoload :ParseYaml, 'faraday_middleware/response/parse_yaml' autoload :ParseDates, 'faraday_middleware/response/parse_dates' autoload :Caching, 'faraday_middleware/response/caching' autoload :Chunked, 'faraday_middleware/response/chunked' autoload :RackCompatible, 'faraday_middleware/rack_compatible' autoload :FollowRedirects, 'faraday_middleware/response/follow_redirects' autoload :Instrumentation, 'faraday_middleware/instrumentation' autoload :Gzip, 'faraday_middleware/gzip' if Faraday::Middleware.respond_to? :register_middleware Faraday::Request.register_middleware \ :oauth => lambda { OAuth }, :oauth2 => lambda { OAuth2 }, :json => lambda { EncodeJson }, :method_override => lambda { MethodOverride } Faraday::Response.register_middleware \ :mashify => lambda { Mashify }, :rashify => lambda { Rashify }, :json => lambda { ParseJson }, :json_fix => lambda { ParseJson::MimeTypeFix }, :xml => lambda { ParseXml }, :marshal => lambda { ParseMarshal }, :yaml => lambda { ParseYaml }, :dates => lambda { ParseDates }, :caching => lambda { Caching }, :follow_redirects => lambda { FollowRedirects }, :chunked => lambda { Chunked } Faraday::Middleware.register_middleware \ :instrumentation => lambda { Instrumentation }, :gzip => lambda { Gzip } end end require 'faraday_middleware/backwards_compatibility' faraday_middleware-0.12.2/lib/faraday_middleware/000077500000000000000000000000001314064102000217415ustar00rootroot00000000000000faraday_middleware-0.12.2/lib/faraday_middleware/addressable_patch.rb000066400000000000000000000011551314064102000257200ustar00rootroot00000000000000require 'addressable/uri' # feature-detect the bug unless Addressable::URI.parse('/?a=1&b=2') === '/?b=2&a=1' # fix `normalized_query` by sorting query key-value pairs # (rejected: https://github.com/sporkmonger/addressable/issues/28) class Addressable::URI alias normalized_query_without_ordering_fix normalized_query def normalized_query fresh = @normalized_query.nil? query = normalized_query_without_ordering_fix if query && fresh @normalized_query = query.split('&', -1).sort_by {|q| q[0..(q.index('=')||-1)] }.join('&') else query end end end end faraday_middleware-0.12.2/lib/faraday_middleware/backwards_compatibility.rb000066400000000000000000000011511314064102000271560ustar00rootroot00000000000000# deprecated constants Faraday::Request.class_eval do autoload :OAuth, 'faraday_middleware/request/oauth' autoload :OAuth2, 'faraday_middleware/request/oauth2' end Faraday::Response.class_eval do autoload :Mashify, 'faraday_middleware/response/mashify' autoload :Rashify, 'faraday_middleware/response/rashify' autoload :ParseJson, 'faraday_middleware/response/parse_json' autoload :ParseXml, 'faraday_middleware/response/parse_xml' autoload :ParseMarshal, 'faraday_middleware/response/parse_marshal' autoload :ParseYaml, 'faraday_middleware/response/parse_yaml' end faraday_middleware-0.12.2/lib/faraday_middleware/gzip.rb000066400000000000000000000037271314064102000232500ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware # Middleware to automatically decompress response bodies. If the # "Accept-Encoding" header wasn't set in the request, this sets it to # "gzip,deflate" and appropriately handles the compressed response from the # server. This resembles what Ruby 1.9+ does internally in Net::HTTP#get. # # This middleware is NOT necessary when these adapters are used: # - net_http on Ruby 1.9+ # - net_http_persistent on Ruby 2.0+ # - em_http class Gzip < Faraday::Middleware dependency 'zlib' ACCEPT_ENCODING = 'Accept-Encoding'.freeze CONTENT_ENCODING = 'Content-Encoding'.freeze CONTENT_LENGTH = 'Content-Length'.freeze SUPPORTED_ENCODINGS = 'gzip,deflate'.freeze RUBY_ENCODING = '1.9'.respond_to?(:force_encoding) def call(env) env[:request_headers][ACCEPT_ENCODING] ||= SUPPORTED_ENCODINGS @app.call(env).on_complete do |response_env| case response_env[:response_headers][CONTENT_ENCODING] when 'gzip' reset_body(response_env, &method(:uncompress_gzip)) when 'deflate' reset_body(response_env, &method(:inflate)) end end end def reset_body(env) env[:body] = yield(env[:body]) env[:response_headers].delete(CONTENT_ENCODING) env[:response_headers][CONTENT_LENGTH] = env[:body].length end def uncompress_gzip(body) io = StringIO.new(body) gzip_reader = if RUBY_ENCODING Zlib::GzipReader.new(io, :encoding => 'ASCII-8BIT') else Zlib::GzipReader.new(io) end gzip_reader.read end def inflate(body) # Inflate as a DEFLATE (RFC 1950+RFC 1951) stream Zlib::Inflate.inflate(body) rescue Zlib::DataError # Fall back to inflating as a "raw" deflate stream which # Microsoft servers return inflate = Zlib::Inflate.new(-Zlib::MAX_WBITS) begin inflate.inflate(body) ensure inflate.close end end end end faraday_middleware-0.12.2/lib/faraday_middleware/instrumentation.rb000066400000000000000000000014741314064102000255370ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware # 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 class Instrumentation < Faraday::Middleware dependency 'active_support/notifications' def initialize(app, options = {}) super(app) @name = options.fetch(:name, 'request.faraday') end def call(env) ::ActiveSupport::Notifications.instrument(@name, env) do @app.call(env) end end end end faraday_middleware-0.12.2/lib/faraday_middleware/rack_compatible.rb000066400000000000000000000051171314064102000254110ustar00rootroot00000000000000require 'stringio' module FaradayMiddleware # Wraps a handler originally written for Rack to make it compatible with Faraday. # # Experimental. Only handles changes in request headers. class RackCompatible def initialize(app, rack_handler, *args) # tiny middleware that decomposes a Faraday::Response to standard Rack # array: [status, headers, body] compatible_app = lambda do |rack_env| env = restore_env(rack_env) response = app.call(env) [response.status, response.headers, Array(response.body)] end @rack = rack_handler.new(compatible_app, *args) end def call(env) rack_env = prepare_env(env) rack_response = @rack.call(rack_env) finalize_response(env, rack_response) end NonPrefixedHeaders = %w[CONTENT_LENGTH CONTENT_TYPE] # faraday to rack-compatible def prepare_env(faraday_env) env = headers_to_rack(faraday_env) url = faraday_env[:url] env['rack.url_scheme'] = url.scheme env['PATH_INFO'] = url.path env['SERVER_PORT'] = url.respond_to?(:inferred_port) ? url.inferred_port : url.port env['QUERY_STRING'] = url.query env['REQUEST_METHOD'] = faraday_env[:method].to_s.upcase env['rack.errors'] ||= StringIO.new env['faraday'] = faraday_env env end def headers_to_rack(env) rack_env = {} env[:request_headers].each do |name, value| name = name.upcase.tr('-', '_') name = "HTTP_#{name}" unless NonPrefixedHeaders.include? name rack_env[name] = value end rack_env end # rack to faraday-compatible def restore_env(rack_env) env = rack_env.fetch('faraday') headers = env[:request_headers] headers.clear rack_env.each do |name, value| next unless String === name && String === value if NonPrefixedHeaders.include? name or name.index('HTTP_') == 0 name = name.sub(/^HTTP_/, '').downcase.tr('_', '-') headers[name] = value end end env[:method] = rack_env['REQUEST_METHOD'].downcase.to_sym env[:rack_errors] = rack_env['rack.errors'] env end def finalize_response(env, rack_response) status, headers, body = rack_response body = body.inject() { |str, part| str << part } headers = Faraday::Utils::Headers.new(headers) unless Faraday::Utils::Headers === headers env.update :status => status.to_i, :body => body, :response_headers => headers env[:response] ||= Faraday::Response.new(env) env[:response] end end end faraday_middleware-0.12.2/lib/faraday_middleware/request/000077500000000000000000000000001314064102000234315ustar00rootroot00000000000000faraday_middleware-0.12.2/lib/faraday_middleware/request/encode_json.rb000066400000000000000000000026011314064102000262430ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware # Request middleware that encodes the body as JSON. # # Processes only requests with matching Content-type or those without a type. # If a request doesn't have a type but has a body, it sets the Content-type # to JSON MIME-type. # # Doesn't try to encode bodies that already are in string form. class EncodeJson < Faraday::Middleware CONTENT_TYPE = 'Content-Type'.freeze MIME_TYPE = 'application/json'.freeze MIME_TYPE_REGEX = /^application\/(vnd\..+\+)?json$/ dependency do require 'json' unless defined?(::JSON) end def call(env) match_content_type(env) do |data| env[:body] = encode data end @app.call env end def encode(data) ::JSON.dump data end def match_content_type(env) if process_request?(env) env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE yield env[:body] unless env[:body].respond_to?(:to_str) end end def process_request?(env) type = request_type(env) has_body?(env) and (type.empty? or MIME_TYPE_REGEX =~ type) end def has_body?(env) body = env[:body] and !(body.respond_to?(:to_str) and body.empty?) end def request_type(env) type = env[:request_headers][CONTENT_TYPE].to_s type = type.split(';', 2).first if type.index(';') type end end end faraday_middleware-0.12.2/lib/faraday_middleware/request/method_override.rb000066400000000000000000000030551314064102000271400ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware # Public: Writes the original HTTP method to "X-Http-Method-Override" header # and sends the request as POST. # # This can be used to work around technical issues with making non-POST # requests, e.g. faulty HTTP client or server router. # # This header is recognized in Rack apps by default, courtesy of the # Rack::MethodOverride module. See # http://rack.rubyforge.org/doc/classes/Rack/MethodOverride.html class MethodOverride < Faraday::Middleware HEADER = "X-Http-Method-Override".freeze # Public: Initialize the middleware. # # app - the Faraday app to wrap # options - (optional) # :rewrite - Array of HTTP methods to rewrite # (default: all but GET and POST) def initialize(app, options = nil) super(app) @methods = options && options.fetch(:rewrite).map { |method| method = method.downcase if method.respond_to? :downcase method.to_sym } end def call(env) method = env[:method] rewrite_request(env, method) if rewrite_request?(method) @app.call(env) end def rewrite_request?(method) if @methods.nil? or @methods.empty? method != :get and method != :post else @methods.include? method end end # Internal: Write the original HTTP method to header, change method to POST. def rewrite_request(env, original_method) env[:request_headers][HEADER] = original_method.to_s.upcase env[:method] = :post end end end faraday_middleware-0.12.2/lib/faraday_middleware/request/oauth.rb000066400000000000000000000052241314064102000251010ustar00rootroot00000000000000require 'faraday' require 'forwardable' module FaradayMiddleware # Public: Uses the simple_oauth library to sign requests according the # OAuth protocol. # # The options for this middleware are forwarded to SimpleOAuth::Header: # :consumer_key, :consumer_secret, :token, :token_secret. All these # parameters are optional. # # The signature is added to the "Authorization" HTTP request header. If the # value for this header already exists, it is not overriden. # # If no Content-Type header is specified, this middleware assumes that # request body parameters should be included while signing the request. # Otherwise, it only includes them if the Content-Type is # "application/x-www-form-urlencoded", as per OAuth 1.0. # # For better performance while signing requests, this middleware should be # positioned before UrlEncoded middleware on the stack, but after any other # body-encoding middleware (such as EncodeJson). class OAuth < Faraday::Middleware dependency 'simple_oauth' AUTH_HEADER = 'Authorization'.freeze CONTENT_TYPE = 'Content-Type'.freeze TYPE_URLENCODED = 'application/x-www-form-urlencoded'.freeze extend Forwardable parser_method = :parse_nested_query parser_module = ::Faraday::Utils.respond_to?(parser_method) ? 'Faraday::Utils' : 'Rack::Utils' def_delegator parser_module, parser_method def initialize(app, options) super(app) @options = options end def call(env) env[:request_headers][AUTH_HEADER] ||= oauth_header(env).to_s if sign_request?(env) @app.call(env) end def oauth_header(env) SimpleOAuth::Header.new env[:method], env[:url].to_s, signature_params(body_params(env)), oauth_options(env) end def sign_request?(env) !!env[:request].fetch(:oauth, true) end def oauth_options(env) if extra = env[:request][:oauth] and extra.is_a? Hash and !extra.empty? @options.merge extra else @options end end def body_params(env) if include_body_params?(env) if env[:body].respond_to?(:to_str) parse_nested_query env[:body] else env[:body] end end || {} end def include_body_params?(env) # see RFC 5849, section 3.4.1.3.1 for details !(type = env[:request_headers][CONTENT_TYPE]) or type == TYPE_URLENCODED end def signature_params(params) params.empty? ? params : params.reject {|k,v| v.respond_to?(:content_type) } end end end # deprecated alias Faraday::Request::OAuth = FaradayMiddleware::OAuth faraday_middleware-0.12.2/lib/faraday_middleware/request/oauth2.rb000066400000000000000000000054131314064102000251630ustar00rootroot00000000000000require 'faraday' require 'forwardable' module FaradayMiddleware # Public: A simple middleware that adds an access token to each request. # # By default, the token is added as both "access_token" query parameter and the # "Authorization" HTTP request header. It can alternatively be added exclusively # as a bearer token "Authorization" header by specifying a "token_type" option # of "bearer". However, an explicit "access_token" parameter or "Authorization" # header for the current request are not overriden. # # Examples # # # configure default token: # OAuth2.new(app, 'abc123') # # # configure query parameter name: # OAuth2.new(app, 'abc123', :param_name => 'my_oauth_token') # # # use bearer token authorization header only # OAuth2.new(app, 'abc123', :token_type => 'bearer') # # # default token value is optional: # OAuth2.new(app, :param_name => 'my_oauth_token') class OAuth2 < Faraday::Middleware PARAM_NAME = 'access_token'.freeze TOKEN_TYPE = 'param'.freeze AUTH_HEADER = 'Authorization'.freeze attr_reader :param_name, :token_type extend Forwardable def_delegators :'Faraday::Utils', :parse_query, :build_query def call(env) params = { param_name => @token }.update query_params(env[:url]) token = params[param_name] if token.respond_to?(:empty?) && !token.empty? case @token_type.downcase when 'param' env[:url].query = build_query params env[:request_headers][AUTH_HEADER] ||= %(Token token="#{token}") when 'bearer' env[:request_headers][AUTH_HEADER] ||= %(Bearer #{token}) end end @app.call env end def initialize(app, token = nil, options = {}) super(app) options, token = token, nil if token.is_a? Hash @token = token && token.to_s @param_name = options.fetch(:param_name, PARAM_NAME).to_s @token_type = options.fetch(:token_type, TOKEN_TYPE).to_s if @token_type == 'param' && @param_name.empty? raise ArgumentError, ":param_name can't be blank" end if options[:token_type].nil? warn "\nWarning: FaradayMiddleware::OAuth2 initialized with default "\ "token_type - token will be added as both a query string parameter "\ "and an Authorization header. In the next major release, tokens will "\ "be added exclusively as an Authorization header by default. Please "\ "visit https://github.com/lostisland/faraday_middleware/wiki for more information." end end def query_params(url) if url.query.nil? or url.query.empty? {} else parse_query url.query end end end end # deprecated alias Faraday::Request::OAuth2 = FaradayMiddleware::OAuth2 faraday_middleware-0.12.2/lib/faraday_middleware/response/000077500000000000000000000000001314064102000235775ustar00rootroot00000000000000faraday_middleware-0.12.2/lib/faraday_middleware/response/caching.rb000066400000000000000000000067651314064102000255360ustar00rootroot00000000000000require 'faraday' require 'forwardable' # fixes normalizing query strings: require 'faraday_middleware/addressable_patch' if defined? ::Addressable::URI module FaradayMiddleware # Public: Caches GET responses and pulls subsequent ones from the cache. class Caching < Faraday::Middleware attr_reader :cache # Internal: List of status codes that can be cached: # * 200 - 'OK' # * 203 - 'Non-Authoritative Information' # * 300 - 'Multiple Choices' # * 301 - 'Moved Permanently' # * 302 - 'Found' # * 404 - 'Not Found' # * 410 - 'Gone' CACHEABLE_STATUS_CODES = [200, 203, 300, 301, 302, 404, 410] extend Forwardable def_delegators :'Faraday::Utils', :parse_query, :build_query # Public: initialize the middleware. # # cache - An object that responds to read, write and fetch (default: nil). # options - An options Hash (default: {}): # :ignore_params - String name or Array names of query params # that should be ignored when forming the cache # key (default: []). # :write_options - Hash of settings that should be passed as the third # options parameter to the cache's #write method. If not # specified, no options parameter will be passed. # # Yields if no cache is given. The block should return a cache object. def initialize(app, cache = nil, options = {}) super(app) options, cache = cache, nil if cache.is_a? Hash and block_given? @cache = cache || yield @options = options end def call(env) if :get == env[:method] if env[:parallel_manager] # callback mode cache_on_complete(env) else # synchronous mode key = cache_key(env) unless response = cache.read(key) and response response = @app.call(env) store_response_in_cache(key, response) end finalize_response(response, env) end else @app.call(env) end end def cache_key(env) url = env[:url].dup if url.query && params_to_ignore.any? params = parse_query url.query params.reject! {|k,| params_to_ignore.include? k } url.query = params.any? ? build_query(params) : nil end url.normalize! url.request_uri end def params_to_ignore @params_to_ignore ||= Array(@options[:ignore_params]).map { |p| p.to_s } end def cache_on_complete(env) key = cache_key(env) if cached_response = cache.read(key) finalize_response(cached_response, env) else # response.status is nil at this point, any checks need to be done inside on_complete block @app.call(env).on_complete do |response_env| store_response_in_cache(key, response_env.response) response_env end end end def store_response_in_cache(key, response) return unless CACHEABLE_STATUS_CODES.include?(response.status) if @options[:write_options] cache.write(key, response, @options[:write_options]) else cache.write(key, response) end end def finalize_response(response, env) response = response.dup if response.frozen? env[:response] = response unless env[:response_headers] env.update response.env # FIXME: omg hax response.instance_variable_set('@env', env) end response end end end faraday_middleware-0.12.2/lib/faraday_middleware/response/chunked.rb000066400000000000000000000017041314064102000255470ustar00rootroot00000000000000require 'faraday_middleware/response_middleware' module FaradayMiddleware # Public: Parse a Transfer-Encoding: Chunked response to just the original data class Chunked < FaradayMiddleware::ResponseMiddleware TRANSFER_ENCODING = 'transfer-encoding'.freeze define_parser do |raw_body| decoded_body = [] until raw_body.empty? chunk_len, raw_body = raw_body.split("\r\n", 2) chunk_len = chunk_len.split(';',2).first.hex break if chunk_len == 0 decoded_body << raw_body[0, chunk_len] # The 2 is to strip the extra CRLF at the end of the chunk raw_body = raw_body[chunk_len + 2, raw_body.length - chunk_len - 2] end decoded_body.join('') end def parse_response?(env) super and chunked_encoding?(env[:response_headers]) end def chunked_encoding?(headers) encoding = headers[TRANSFER_ENCODING] and encoding.split(',').include?('chunked') end end end faraday_middleware-0.12.2/lib/faraday_middleware/response/follow_redirects.rb000066400000000000000000000107121314064102000274730ustar00rootroot00000000000000require 'faraday' require 'set' module FaradayMiddleware # Public: Exception thrown when the maximum amount of requests is exceeded. class RedirectLimitReached < Faraday::Error::ClientError attr_reader :response def initialize(response) super "too many redirects; last one to: #{response['location']}" @response = response end end # Public: Follow HTTP 301, 302, 303, 307, and 308 redirects. # # For HTTP 301, 302, and 303, the original GET, POST, PUT, DELETE, or PATCH # request gets converted into a GET. With `:standards_compliant => true`, # however, the HTTP method after 301/302 remains unchanged. This allows you # to opt into HTTP/1.1 compliance and act unlike the major web browsers. # # This middleware currently only works with synchronous requests; i.e. it # doesn't support parallelism. # # If you wish to persist cookies across redirects, you could use # the faraday-cookie_jar gem: # # Faraday.new(:url => url) do |faraday| # faraday.use FaradayMiddleware::FollowRedirects # faraday.use :cookie_jar # faraday.adapter Faraday.default_adapter # end class FollowRedirects < Faraday::Middleware # HTTP methods for which 30x redirects can be followed ALLOWED_METHODS = Set.new [:head, :options, :get, :post, :put, :patch, :delete] # HTTP redirect status codes that this middleware implements REDIRECT_CODES = Set.new [301, 302, 303, 307, 308] # Keys in env hash which will get cleared between requests ENV_TO_CLEAR = Set.new [:status, :response, :response_headers] # Default value for max redirects followed FOLLOW_LIMIT = 3 # Regex that matches characters that need to be escaped in URLs, sans # the "%" character which we assume already represents an escaped sequence. URI_UNSAFE = /[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]%]/ # Public: Initialize the middleware. # # options - An options Hash (default: {}): # :limit - A Numeric redirect limit (default: 3) # :standards_compliant - A Boolean indicating whether to respect # the HTTP spec when following 301/302 # (default: false) # :callback - A callable that will be called on redirects # with the old and new envs def initialize(app, options = {}) super(app) @options = options @convert_to_get = Set.new [303] @convert_to_get << 301 << 302 unless standards_compliant? end def call(env) perform_with_redirection(env, follow_limit) end private def convert_to_get?(response) ![:head, :options].include?(response.env[:method]) && @convert_to_get.include?(response.status) end def perform_with_redirection(env, follows) request_body = env[:body] response = @app.call(env) response.on_complete do |response_env| if follow_redirect?(response_env, response) raise RedirectLimitReached, response if follows.zero? new_request_env = update_env(response_env.dup, request_body, response) callback.call(response_env, new_request_env) if callback response = perform_with_redirection(new_request_env, follows - 1) end end response end def update_env(env, request_body, response) env[:url] += safe_escape(response['location'] || '') if convert_to_get?(response) env[:method] = :get env[:body] = nil else env[:body] = request_body end ENV_TO_CLEAR.each {|key| env.delete key } env end def follow_redirect?(env, response) ALLOWED_METHODS.include? env[:method] and REDIRECT_CODES.include? response.status end def follow_limit @options.fetch(:limit, FOLLOW_LIMIT) end def standards_compliant? @options.fetch(:standards_compliant, false) end def callback @options[:callback] end # Internal: escapes unsafe characters from an URL which might be a path # component only or a fully qualified URI so that it can be joined onto an # URI:HTTP using the `+` operator. Doesn't escape "%" characters so to not # risk double-escaping. def safe_escape(uri) uri = uri.split('#')[0] # we want to remove the fragment if present uri.to_s.gsub(URI_UNSAFE) { |match| '%' + match.unpack('H2' * match.bytesize).join('%').upcase } end end end faraday_middleware-0.12.2/lib/faraday_middleware/response/mashify.rb000066400000000000000000000014201314064102000255610ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware # Public: Converts parsed response bodies to a Hashie::Mash if they were of # Hash or Array type. class Mashify < Faraday::Response::Middleware attr_accessor :mash_class class << self attr_accessor :mash_class end dependency do require 'hashie/mash' self.mash_class = ::Hashie::Mash end def initialize(app = nil, options = {}) super(app) self.mash_class = options[:mash_class] || self.class.mash_class end def parse(body) case body when Hash mash_class.new(body) when Array body.map { |item| parse(item) } else body end end end end # deprecated alias Faraday::Response::Mashify = FaradayMiddleware::Mashify faraday_middleware-0.12.2/lib/faraday_middleware/response/parse_dates.rb000066400000000000000000000015411314064102000264170ustar00rootroot00000000000000require "time" require "faraday" module FaradayMiddleware # Parse dates from response body class ParseDates < ::Faraday::Response::Middleware ISO_DATE_FORMAT = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|((\+|-)\d{2}:?\d{2}))\Z/m def initialize(app, options = {}) @regexp = options[:match] || ISO_DATE_FORMAT super(app) end def call(env) response = @app.call(env) parse_dates! response.env[:body] response end private def parse_dates!(value) case value when Hash value.each do |key, element| value[key] = parse_dates!(element) end when Array value.each_with_index do |element, index| value[index] = parse_dates!(element) end when @regexp Time.parse(value) else value end end end end faraday_middleware-0.12.2/lib/faraday_middleware/response/parse_json.rb000066400000000000000000000027431314064102000262750ustar00rootroot00000000000000require 'faraday_middleware/response_middleware' module FaradayMiddleware # Public: Parse response bodies as JSON. class ParseJson < ResponseMiddleware dependency do require 'json' unless defined?(::JSON) end define_parser do |body, parser_options| ::JSON.parse(body, parser_options || {}) unless body.strip.empty? end # Public: Override the content-type of the response with "application/json" # if the response body looks like it might be JSON, i.e. starts with an # open bracket. # # This is to fix responses from certain API providers that insist on serving # JSON with wrong MIME-types such as "text/javascript". class MimeTypeFix < ResponseMiddleware MIME_TYPE = 'application/json'.freeze def process_response(env) old_type = env[:response_headers][CONTENT_TYPE].to_s new_type = MIME_TYPE.dup new_type << ';' << old_type.split(';', 2).last if old_type.index(';') env[:response_headers][CONTENT_TYPE] = new_type end BRACKETS = %w- [ { - WHITESPACE = [ " ", "\n", "\r", "\t" ] def parse_response?(env) super and BRACKETS.include? first_char(env[:body]) end def first_char(body) idx = -1 begin char = body[idx += 1] char = char.chr if char end while char and WHITESPACE.include? char char end end end end # deprecated alias Faraday::Response::ParseJson = FaradayMiddleware::ParseJson faraday_middleware-0.12.2/lib/faraday_middleware/response/parse_marshal.rb000066400000000000000000000005471314064102000267530ustar00rootroot00000000000000require 'faraday_middleware/response_middleware' module FaradayMiddleware # Public: Restore marshalled Ruby objects in response bodies. class ParseMarshal < ResponseMiddleware define_parser do |body| ::Marshal.load(body) unless body.empty? end end end # deprecated alias Faraday::Response::ParseMarshal = FaradayMiddleware::ParseMarshal faraday_middleware-0.12.2/lib/faraday_middleware/response/parse_xml.rb000066400000000000000000000005761314064102000261260ustar00rootroot00000000000000require 'faraday_middleware/response_middleware' module FaradayMiddleware # Public: parses response bodies with MultiXml. class ParseXml < ResponseMiddleware dependency 'multi_xml' define_parser do |body, parser_options| ::MultiXml.parse(body, parser_options || {}) end end end # deprecated alias Faraday::Response::ParseXml = FaradayMiddleware::ParseXml faraday_middleware-0.12.2/lib/faraday_middleware/response/parse_yaml.rb000066400000000000000000000023611314064102000262620ustar00rootroot00000000000000require 'faraday_middleware/response_middleware' module FaradayMiddleware # Public: Parse response bodies as YAML. # # Warning: This is not backwards compatible with versions of this middleware prior to # faraday_middleware v0.12 - prior to this version, we used YAML.load rather than # YAMl.safe_load, which exposes serious remote code execution risks - see # https://github.com/ruby/psych/issues/119 for details. If you're sure you can trust # YAML you're passing, you can set up an unsafe version of this middleware as follows: # # class UnsafelyParseYaml < FaradayMiddleware::ResponseMiddleware # dependency do # require 'yaml' # end # # define_parser do |body| # YAML.load body # end # end # # Faraday.new(..) do |config| # config.use UnsafelyParseYaml # ... # end class ParseYaml < ResponseMiddleware dependency 'safe_yaml/load' define_parser do |body, parser_options| if SafeYAML::YAML_ENGINE == 'psych' SafeYAML.load(body, nil, parser_options || {}) else SafeYAML.load(body, parser_options || {}) end end end end # deprecated alias Faraday::Response::ParseYaml = FaradayMiddleware::ParseYaml faraday_middleware-0.12.2/lib/faraday_middleware/response/rashify.rb000066400000000000000000000005721314064102000255750ustar00rootroot00000000000000require 'faraday_middleware/response/mashify' module FaradayMiddleware # Public: Converts parsed response bodies to a Hashie::Rash if they were of # Hash or Array type. class Rashify < Mashify dependency do require 'rash' self.mash_class = ::Hashie::Mash::Rash end end end # deprecated alias Faraday::Response::Rashify = FaradayMiddleware::Rashify faraday_middleware-0.12.2/lib/faraday_middleware/response_middleware.rb000066400000000000000000000055241314064102000263270ustar00rootroot00000000000000require 'faraday' module FaradayMiddleware # Internal: The base class for middleware that parses responses. class ResponseMiddleware < Faraday::Middleware CONTENT_TYPE = 'Content-Type'.freeze class << self attr_accessor :parser end # Store a Proc that receives the body and returns the parsed result. def self.define_parser(parser = nil) @parser = parser || Proc.new end def self.inherited(subclass) super subclass.load_error = self.load_error if subclass.respond_to? :load_error= subclass.parser = self.parser end def initialize(app = nil, options = {}) super(app) @options = options @parser_options = options[:parser_options] @content_types = Array(options[:content_type]) end def call(environment) @app.call(environment).on_complete do |env| if process_response_type?(response_type(env)) and parse_response?(env) process_response(env) end end end def process_response(env) env[:raw_body] = env[:body] if preserve_raw?(env) env[:body] = parse(env[:body]) rescue Faraday::Error::ParsingError => err raise Faraday::Error::ParsingError.new(err, env[:response]) end # Parse the response body. # # Instead of overriding this method, consider using `define_parser`. def parse(body) if self.class.parser begin self.class.parser.call(body, @parser_options) rescue StandardError, SyntaxError => err raise err if err.is_a? SyntaxError and err.class.name != 'Psych::SyntaxError' raise Faraday::Error::ParsingError, err end else body end end def response_type(env) type = env[:response_headers][CONTENT_TYPE].to_s type = type.split(';', 2).first if type.index(';') type end def process_response_type?(type) @content_types.empty? or @content_types.any? { |pattern| pattern.is_a?(Regexp) ? type =~ pattern : type == pattern } end def parse_response?(env) env[:body].respond_to? :to_str end def preserve_raw?(env) env[:request].fetch(:preserve_raw, @options[:preserve_raw]) end end # DRAGONS module OptionsExtension attr_accessor :preserve_raw def to_hash super.update(:preserve_raw => preserve_raw) end def each return to_enum(:each) unless block_given? super yield :preserve_raw, preserve_raw end def fetch(key, *args) if :preserve_raw == key value = __send__(key) value.nil? ? args.fetch(0) : value else super end end end if defined?(Faraday::RequestOptions) begin Faraday::RequestOptions.from(:preserve_raw => true) rescue NoMethodError Faraday::RequestOptions.send(:include, OptionsExtension) end end end faraday_middleware-0.12.2/lib/faraday_middleware/version.rb000066400000000000000000000001361314064102000237530ustar00rootroot00000000000000module FaradayMiddleware VERSION = '0.12.2' unless defined?(FaradayMiddleware::VERSION) end faraday_middleware-0.12.2/script/000077500000000000000000000000001314064102000166735ustar00rootroot00000000000000faraday_middleware-0.12.2/script/bootstrap000077500000000000000000000001141314064102000206320ustar00rootroot00000000000000#!/bin/bash set -e bundle install --path vendor/bundle bundle binstub rake faraday_middleware-0.12.2/script/console000077500000000000000000000002541314064102000202640ustar00rootroot00000000000000#!/usr/bin/env bash # Usage: script/console # Starts an IRB console with this library loaded. gemspec="$(ls *.gemspec | head -1)" exec bundle exec irb -r "${gemspec%.*}" faraday_middleware-0.12.2/script/package000077500000000000000000000002301314064102000202070ustar00rootroot00000000000000#!/usr/bin/env bash # Usage: script/gem # Updates the gemspec and builds a new gem in the pkg directory. mkdir -p pkg gem build *.gemspec mv *.gem pkg faraday_middleware-0.12.2/script/release000077500000000000000000000006041314064102000202410ustar00rootroot00000000000000#!/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 --allow-empty -a -m "faraday_middleware $version" git tag "v${version}" git push origin HEAD "v${version}" gem push pkg/*-${version}.gem faraday_middleware-0.12.2/spec/000077500000000000000000000000001314064102000163215ustar00rootroot00000000000000faraday_middleware-0.12.2/spec/helper.rb000066400000000000000000000027311314064102000201300ustar00rootroot00000000000000if ENV['COVERAGE'] require 'simplecov' SimpleCov.formatter = Class.new do def format(result) SimpleCov::Formatter::HTMLFormatter.new.format(result) unless ENV['CI'] File.open('coverage/covered_percent', 'w') do |f| f.printf "%.2f", result.source_files.covered_percent end end end SimpleCov.start do # add_filter 'faraday_middleware.rb' add_filter 'backwards_compatibility.rb' end end require 'rspec' require 'webmock/rspec' require 'faraday' module EnvCompatibility def faraday_env(env) if defined?(Faraday::Env) Faraday::Env.from(env) else env end end end module ResponseMiddlewareExampleGroup def self.included(base) base.let(:options) { Hash.new } base.let(:headers) { Hash.new } base.let(:middleware) { described_class.new(lambda {|env| Faraday::Response.new(env) }, options) } end def process(body, content_type = nil, options = {}) env = { :body => body, :request => options, :request_headers => Faraday::Utils::Headers.new, :response_headers => Faraday::Utils::Headers.new(headers) } env[:response_headers]['content-type'] = content_type if content_type yield(env) if block_given? middleware.call(faraday_env(env)) end end RSpec.configure do |config| config.include EnvCompatibility config.include ResponseMiddlewareExampleGroup, :type => :response config.expect_with :rspec do |c| c.syntax = :expect end end faraday_middleware-0.12.2/spec/integration/000077500000000000000000000000001314064102000206445ustar00rootroot00000000000000faraday_middleware-0.12.2/spec/integration/follow_redirects_spec.rb000066400000000000000000000050471314064102000255570ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/follow_redirects' describe FaradayMiddleware::FollowRedirects do it 'redirects on 301' do stub_request(:get, 'http://www.site-a.com/').to_return( :status => 301, :headers => {'Location' => 'https://www.site-b.com/'}) stub_request(:get, 'https://www.site-b.com/') connection = Faraday.new do |conn| conn.use FaradayMiddleware::FollowRedirects conn.adapter Faraday.default_adapter end response = connection.get 'http://www.site-a.com' expect(response.env[:url].to_s).to eq('https://www.site-b.com/') end it 'redirects on 302' do stub_request(:get, 'http://www.site-a.com/').to_return( :status => 302, :headers => {'Location' => 'https://www.site-b.com/'}) stub_request(:get, 'https://www.site-b.com/') connection = Faraday.new do |conn| conn.use FaradayMiddleware::FollowRedirects conn.adapter Faraday.default_adapter end response = connection.get 'http://www.site-a.com' expect(response.env[:url].to_s).to eq('https://www.site-b.com/') end it 'redirects on 303' do stub_request(:get, 'http://www.site-a.com/').to_return( :status => 303, :headers => {'Location' => 'https://www.site-b.com/'}) stub_request(:get, 'https://www.site-b.com/') connection = Faraday.new do |conn| conn.use FaradayMiddleware::FollowRedirects conn.adapter Faraday.default_adapter end response = connection.get 'http://www.site-a.com' expect(response.env[:url].to_s).to eq('https://www.site-b.com/') end it 'redirects on 307' do stub_request(:get, 'http://www.site-a.com/').to_return( :status => 307, :headers => {'Location' => 'https://www.site-b.com/'}) stub_request(:get, 'https://www.site-b.com/') connection = Faraday.new do |conn| conn.use FaradayMiddleware::FollowRedirects conn.adapter Faraday.default_adapter end response = connection.get 'http://www.site-a.com' expect(response.env[:url].to_s).to eq('https://www.site-b.com/') end it 'redirects on 308' do stub_request(:get, 'http://www.site-a.com/').to_return( :status => 308, :headers => {'Location' => 'https://www.site-b.com/'}) stub_request(:get, 'https://www.site-b.com/') connection = Faraday.new do |conn| conn.use FaradayMiddleware::FollowRedirects conn.adapter Faraday.default_adapter end response = connection.get 'http://www.site-a.com' expect(response.env[:url].to_s).to eq('https://www.site-b.com/') end end faraday_middleware-0.12.2/spec/unit/000077500000000000000000000000001314064102000173005ustar00rootroot00000000000000faraday_middleware-0.12.2/spec/unit/caching_spec.rb000066400000000000000000000134061314064102000222370ustar00rootroot00000000000000require 'helper' require 'forwardable' require 'fileutils' require 'rack/cache' require 'faraday' require 'faraday_middleware/response/caching' require 'faraday_middleware/rack_compatible' describe FaradayMiddleware::Caching do before do @cache = TestCache.new request_count = 0 response = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "request:#{request_count+=1}"] } broken = lambda { |env| [500, {'Content-Type' => 'text/plain'}, "request:#{request_count+=1}"] } @conn = Faraday.new do |b| b.use CachingLint b.use FaradayMiddleware::Caching, @cache, options b.adapter :test do |stub| stub.get('/', &response) stub.get('/?foo=bar', &response) stub.post('/', &response) stub.get('/other', &response) stub.get('/broken', &broken) end end end let(:options) { {} } extend Forwardable def_delegators :@conn, :get, :post it 'caches get requests' do expect(get('/').body).to eq('request:1') expect(get('/').body).to eq('request:1') expect(get('/other').body).to eq('request:2') expect(get('/other').body).to eq('request:2') end it 'includes request params in the response' do get('/') # make cache response = get('/') expect(response.env[:method]).to eq(:get) expect(response.env[:url].request_uri).to eq('/') end it 'caches requests with query params' do expect(get('/').body).to eq('request:1') expect(get('/?foo=bar').body).to eq('request:2') expect(get('/?foo=bar').body).to eq('request:2') expect(get('/').body).to eq('request:1') end it 'does not cache post requests' do expect(post('/').body).to eq('request:1') expect(post('/').body).to eq('request:2') expect(post('/').body).to eq('request:3') end it 'does not cache responses with invalid status code' do expect(get('/broken').body).to eq('request:1') expect(get('/broken').body).to eq('request:2') end context ":ignore_params" do let(:options) { {:ignore_params => %w[ utm_source utm_term ]} } it "strips ignored parameters from cache_key" do expect(get('/').body).to eq('request:1') expect(get('/?utm_source=a').body).to eq('request:1') expect(get('/?utm_source=a&utm_term=b').body).to eq('request:1') expect(get('/?utm_source=a&utm_term=b&foo=bar').body).to eq('request:2') expect(get('/?foo=bar').body).to eq('request:2') end end context ":write_options" do let(:options) { {:write_options => {:expires_in => 9000 } } } it "passes on the options when writing to the cache" do expect(@cache).to receive(:write).with("/", instance_of(Faraday::Response), options[:write_options]) get('/') end context "with no :write_options" do let(:options) { {} } it "doesn't pass a third options parameter to the cache's #write" do expect(@cache).to receive(:write).with("/", instance_of(Faraday::Response)) get('/') end end end class TestCache < Hash def read(key) if cached = self[key] Marshal.load(cached) end end def write(key, data, options = nil) self[key] = Marshal.dump(data) end def fetch(key) read(key) || yield.tap { |data| write(key, data) } end end class CachingLint < Struct.new(:app) def call(env) app.call(env).on_complete do raise "no headers" unless env[:response_headers].is_a? Hash raise "no response" unless env[:response].is_a? Faraday::Response # raise "env not identical" unless env[:response].env.object_id == env.object_id end end end end # RackCompatible + Rack::Cache describe FaradayMiddleware::RackCompatible, 'caching' do include FileUtils CACHE_DIR = File.expand_path('../../tmp/cache', __FILE__) before do rm_r CACHE_DIR if File.exist? CACHE_DIR # force reinitializing cache dirs Rack::Cache::Storage.instance.clear request_count = 0 response = lambda { |env| [200, { 'Content-Type' => 'text/plain', 'Cache-Control' => 'public, max-age=900', }, "request:#{request_count+=1}"] } @conn = Faraday.new do |b| b.use RackErrorsComplainer b.use FaradayMiddleware::RackCompatible, Rack::Cache::Context, :metastore => "file:#{CACHE_DIR}/rack/meta", :entitystore => "file:#{CACHE_DIR}/rack/body", :verbose => true b.adapter :test do |stub| stub.get('/', &response) stub.post('/', &response) end end end extend Forwardable def_delegators :@conn, :get, :post it 'caches get requests' do response = get('/', :user_agent => 'test') expect(response.body).to eq('request:1') expect(response.env[:method]).to eq(:get) expect(response.status).to eq(200) response = get('/', :user_agent => 'test') expect(response.body).to eq('request:1') expect(response['content-type']).to eq('text/plain') expect(response.env[:method]).to eq(:get) expect(response.env[:request].respond_to?(:fetch)).to be true expect(response.status). to eq(200) expect(post('/').body).to eq('request:2') end it 'does not cache post requests' do expect(get('/').body).to eq('request:1') expect(post('/').body).to eq('request:2') expect(post('/').body).to eq('request:3') end # middleware to check whether "rack.errors" is free of error reports class RackErrorsComplainer < Struct.new(:app) def call(env) response = app.call(env) error_stream = env[:rack_errors] if error_stream.respond_to?(:string) && error_stream.string.include?('error') raise %(unexpected error in 'rack.errors': %p) % error_stream.string end response end end end faraday_middleware-0.12.2/spec/unit/chunked_spec.rb000066400000000000000000000043611314064102000222640ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/chunked' describe FaradayMiddleware::Chunked, :type => :response do context "no transfer-encoding" do it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "doesn't change an empty body" do expect(process('').body).to eq('') end it "doesn't change a normal body" do expect(process('asdf').body).to eq('asdf') end end context "transfer-encoding gzip" do let(:headers) { {"transfer-encoding" => "gzip"}} it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "doesn't change an empty body" do expect(process('').body).to eq('') end it "doesn't change a normal body" do expect(process('asdf').body).to eq('asdf') end end context "transfer-encoding chunked" do let(:headers) { {"transfer-encoding" => "chunked"}} it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "doesn't change an empty body" do expect(process('').body).to eq('') end it "parses a basic chunked body" do expect(process("10\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj') end it "parses a chunked body with no ending chunk" do expect(process("10\r\nasdfghjklasdfghj\r\n").body).to eq('asdfghjklasdfghj') end it "parses a chunked body with no trailing CRLF on the data chunk" do expect(process("10\r\nasdfghjklasdfghj0\r\n").body).to eq('asdfghjklasdfghj') end it "parses a chunked body with an extension" do expect(process("10;foo=bar\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj') end it "parses a chunked body with two extensions" do expect(process("10;foo=bar;bar=baz\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj') end it "parses a chunked body with two chunks" do expect(process("8\r\nasdfghjk\r\n8\r\nlasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj') end end context "transfer-encoding chunked,chunked" do let(:headers) { {"transfer-encoding" => "chunked,chunked"}} it "parses a basic chunked body" do expect(process("10\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj') end end end faraday_middleware-0.12.2/spec/unit/encode_json_spec.rb000066400000000000000000000050431314064102000231270ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/request/encode_json' describe FaradayMiddleware::EncodeJson do let(:middleware) { described_class.new(lambda{|env| env}) } def process(body, content_type = nil) env = {:body => body, :request_headers => Faraday::Utils::Headers.new} env[:request_headers]['content-type'] = content_type if content_type middleware.call(faraday_env(env)) end def result_body() result[:body] end def result_type() result[:request_headers]['content-type'] end context "no body" do let(:result) { process(nil) } it "doesn't change body" do expect(result_body).to be_nil end it "doesn't add content type" do expect(result_type).to be_nil end end context "empty body" do let(:result) { process('') } it "doesn't change body" do expect(result_body).to be_empty end it "doesn't add content type" do expect(result_type).to be_nil end end context "string body" do let(:result) { process('{"a":1}') } it "doesn't change body" do expect(result_body).to eq('{"a":1}') end it "adds content type" do expect(result_type).to eq('application/json') end end context "object body" do let(:result) { process({:a => 1}) } it "encodes body" do expect(result_body).to eq('{"a":1}') end it "adds content type" do expect(result_type).to eq('application/json') end end context "empty object body" do let(:result) { process({}) } it "encodes body" do expect(result_body).to eq('{}') end end context "object body with json type" do let(:result) { process({:a => 1}, 'application/json; charset=utf-8') } it "encodes body" do expect(result_body).to eq('{"a":1}') end it "doesn't change content type" do expect(result_type).to eq('application/json; charset=utf-8') end end context "object body with vendor json type" do let(:result) { process({:a => 1}, 'application/vnd.myapp.v1+json; charset=utf-8') } it "encodes body" do expect(result_body).to eq('{"a":1}') end it "doesn't change content type" do expect(result_type).to eq('application/vnd.myapp.v1+json; charset=utf-8') end end context "object body with incompatible type" do let(:result) { process({:a => 1}, 'application/xml; charset=utf-8') } it "doesn't change body" do expect(result_body).to eq({:a => 1}) end it "doesn't change content type" do expect(result_type).to eq('application/xml; charset=utf-8') end end end faraday_middleware-0.12.2/spec/unit/follow_redirects_spec.rb000066400000000000000000000222541314064102000242120ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/follow_redirects' require 'faraday' # expose a method in Test adapter that should have been public Faraday::Adapter::Test::Stubs.class_eval { public :new_stub } describe FaradayMiddleware::FollowRedirects do let(:middleware_options) { Hash.new } shared_examples_for "a successful redirection" do |status_code| it "follows the redirection for a GET request" do expect(connection do |stub| stub.get('/permanent') { [status_code, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end.get('/permanent').body).to eq 'fin' end it "follows the redirection for a HEAD request" do expect(connection do |stub| stub.head('/permanent') { [status_code, {'Location' => '/found'}, ''] } stub.head('/found') { [200, {'Content-Type' => 'text/plain'}, ''] } end.head('/permanent').status).to eq 200 end it "follows the redirection for a OPTIONS request" do expect(connection do |stub| stub.new_stub(:options, '/permanent') { [status_code, {'Location' => '/found'}, ''] } stub.new_stub(:options, '/found') { [200, {'Content-Type' => 'text/plain'}, ''] } end.run_request(:options, '/permanent', nil, nil).status).to eq 200 end it "tolerates invalid characters in redirect location" do unescaped_location = '/found?action_type_map=["og.likes!%20you"]' escaped_location = '/found?action_type_map=[%22og.likes!%20you%22]' expect(connection { |stub| stub.get('/') { [status_code, {'Location' => unescaped_location}, ''] } stub.get(escaped_location) { [200, {'Content-Type' => 'text/plain'}, 'fin'] } }.get('/').body).to eq('fin') end end shared_examples_for "a forced GET redirection" do |status_code| [:put, :post, :delete, :patch].each do |method| it "a #{method.to_s.upcase} request is converted to a GET" do expect(connection do |stub| stub.new_stub(method, '/redirect') { [status_code, {'Location' => '/found'}, 'elsewhere'] } stub.get('/found') { |env| body = env[:body] and body.empty? && (body = nil) [200, {'Content-Type' => 'text/plain'}, body.inspect] } end.run_request(method, '/redirect', 'request data', nil).body).to eq('nil') end end end shared_examples_for "a replayed redirection" do |status_code| it "redirects with the original request headers" do conn = connection do |stub| stub.get('/redirect') { [status_code, {'Location' => '/found'}, ''] } stub.get('/found') { |env| [200, {'Content-Type' => 'text/plain'}, env[:request_headers]['X-Test-Value']] } end response = conn.get('/redirect') { |req| req.headers['X-Test-Value'] = 'success' } expect(response.body).to eq('success') end [:put, :post, :delete, :patch].each do |method| it "replays a #{method.to_s.upcase} request" do expect(connection do |stub| stub.new_stub(method, '/redirect') { [status_code, {'Location' => '/found'}, ''] } stub.new_stub(method, '/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end.run_request(method, '/redirect', nil, nil).body).to eq 'fin' end end [:put, :post, :patch].each do |method| it "forwards request body for a #{method.to_s.upcase} request" do conn = connection do |stub| stub.new_stub(method, '/redirect') { [status_code, {'Location' => '/found'}, ''] } stub.new_stub(method, '/found') { |env| [200, {'Content-Type' => 'text/plain'}, env[:body]] } end response = conn.run_request(method, '/redirect', 'original data', nil) expect(response.body).to eq('original data') end end end it "returns non-redirect response results" do expect(connection do |stub| stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end.get('/found').body).to eq 'fin' end it "follows a single redirection" do expect(connection do |stub| stub.get('/') { [301, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end.get('/').body).to eq 'fin' end it "follows many redirections" do expect(connection do |stub| stub.get('/') { [301, {'Location' => '/redirect1'}, ''] } stub.get('/redirect1') { [301, {'Location' => '/redirect2'}, ''] } stub.get('/redirect2') { [301, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end.get('/').body).to eq 'fin' end it "raises a FaradayMiddleware::RedirectLimitReached after 3 redirections (by default)" do conn = connection do |stub| stub.get('/') { [301, {'Location' => '/redirect1'}, ''] } stub.get('/redirect1') { [301, {'Location' => '/redirect2'}, ''] } stub.get('/redirect2') { [301, {'Location' => '/redirect3'}, ''] } stub.get('/redirect3') { [301, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end expect{ conn.get('/') }.to raise_error(FaradayMiddleware::RedirectLimitReached) end it "raises a FaradayMiddleware::RedirectLimitReached after the initialized limit" do conn = connection(:limit => 1) do |stub| stub.get('/') { [301, {'Location' => '/redirect1'}, ''] } stub.get('/redirect1') { [301, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end expect{ conn.get('/') }.to raise_error(FaradayMiddleware::RedirectLimitReached) end it "ignore fragments in the Location header" do expect(connection do |stub| stub.get('/') { [301, {'Location' => '/found#fragment'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end.get('/').body).to eq 'fin' end described_class::REDIRECT_CODES.each do |code| context "for an HTTP #{code} response" do it "raises a FaradayMiddleware::RedirectLimitReached when Location header is missing" do conn = connection do |stub| stub.get('/') { [code, {}, ''] } end expect{ conn.get('/') }.to raise_error(FaradayMiddleware::RedirectLimitReached) end end end [301, 302].each do |code| context "for an HTTP #{code} response" do it_behaves_like 'a successful redirection', code context "by default" do it_behaves_like 'a forced GET redirection', code end context "with standards compliancy enabled" do let(:middleware_options) { { :standards_compliant => true } } it_behaves_like 'a replayed redirection', code end end end context "for an HTTP 303 response" do context "by default" do it_behaves_like 'a successful redirection', 303 it_behaves_like 'a forced GET redirection', 303 end context "with standards compliancy enabled" do let(:middleware_options) { { :standards_compliant => true } } it_behaves_like 'a successful redirection', 303 it_behaves_like 'a forced GET redirection', 303 end end context "for an HTTP 307 response" do context "by default" do it_behaves_like 'a successful redirection', 307 it_behaves_like 'a replayed redirection', 307 end context "with standards compliancy enabled" do let(:middleware_options) { { :standards_compliant => true } } it_behaves_like 'a successful redirection', 307 it_behaves_like 'a replayed redirection', 307 end end context "for an HTTP 308 response" do context "by default" do it_behaves_like 'a successful redirection', 308 it_behaves_like 'a replayed redirection', 308 end context "with standards compliancy enabled" do let(:middleware_options) { { :standards_compliant => true } } it_behaves_like 'a successful redirection', 308 it_behaves_like 'a replayed redirection', 308 end end context "with a callback" do it "calls the callback" do from, to = nil, nil callback = lambda { |old, new| from, to = old[:url].path, new[:url].path } conn = connection(:callback => callback) do |stub| stub.get('/redirect') { [301, {'Location' => '/found'}, ''] } stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] } end conn.get('/redirect') expect([from, to]).to eq ['/redirect', '/found'] end end # checks env hash in request phase for basic validity class Lint < Struct.new(:app) def call(env) if env[:status] or env[:response] or env[:response_headers] raise "invalid request: #{env.inspect}" end if defined?(Faraday::Env) && !env.is_a?(Faraday::Env) raise "expected Faraday::Env, got #{env.class}" end app.call(env) end end private def connection(options = middleware_options) Faraday.new do |c| c.use described_class, options c.use Lint c.adapter :test do |stub| yield(stub) if block_given? end end end end faraday_middleware-0.12.2/spec/unit/gzip_spec.rb000066400000000000000000000050471314064102000216160ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/gzip' describe FaradayMiddleware::Gzip, :type => :response do let(:middleware) { described_class.new(lambda { |env| Faraday::Response.new(env) }) } context 'request' do it 'sets the Accept-Encoding request header' do env = process('').env expect(env[:request_headers][:accept_encoding]).to eq('gzip,deflate') end it 'doesnt overwrite existing Accept-Encoding request header' do env = process('') { |e| e[:request_headers][:accept_encoding] = 'zopfli' }.env expect(env[:request_headers][:accept_encoding]).to eq('zopfli') end end context 'response' do let(:uncompressed_body) { "RspecHello, spec!" } let(:gzipped_body) { io = StringIO.new gz = Zlib::GzipWriter.new(io) gz.write(uncompressed_body) gz.close res = io.string res.force_encoding('BINARY') if res.respond_to?(:force_encoding) res } let(:deflated_body) { Zlib::Deflate.deflate(uncompressed_body) } let(:raw_deflated_body) { z = Zlib::Deflate.new(Zlib::DEFAULT_COMPRESSION, -Zlib::MAX_WBITS) compressed_body = z.deflate(uncompressed_body, Zlib::FINISH) z.close compressed_body } shared_examples 'compressed response' do it 'uncompresses the body' do expect(process(body).body).to eq(uncompressed_body) end it 'sets the Content-Length' do expect(process(body).headers['Content-Length']).to eq(uncompressed_body.length) end it 'removes the Content-Encoding' do expect(process(body).headers['Content-Encoding']).to be_nil end end context 'gzipped response' do let(:body) { gzipped_body } let(:headers) { {'Content-Encoding' => 'gzip', 'Content-Length' => body.length} } it_behaves_like 'compressed response' end context 'deflated response' do let(:body) { deflated_body } let(:headers) { {'Content-Encoding' => 'deflate', 'Content-Length' => body.length} } it_behaves_like 'compressed response' end context 'raw deflated response' do let(:body) { raw_deflated_body } let(:headers) { {'Content-Encoding' => 'deflate', 'Content-Length' => body.length} } it_behaves_like 'compressed response' end context 'identity response' do let(:body) { uncompressed_body } it 'does not modify the body' do expect(process(body).body).to eq(uncompressed_body) end end end end faraday_middleware-0.12.2/spec/unit/mashify_spec.rb000066400000000000000000000037711314064102000223070ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/mashify' describe FaradayMiddleware::Mashify do MyMash = Struct.new(:body) context "when used", :type => :response do it "creates a Hashie::Mash from the body" do body = { "name" => "Erik Michaels-Ober", "username" => "sferik" } me = process(body).body expect(me.name).to eq("Erik Michaels-Ober") expect(me.username).to eq("sferik") end it "handles strings" do body = "Most amazing string EVER" me = process(body).body expect(me).to eq("Most amazing string EVER") end it "handles arrays" do body = [123, 456] values = process(body).body expect(values).to eq([123, 456]) end it "handles arrays of hashes" do body = [{ "username" => "sferik" }, { "username" => "pengwynn" }] us = process(body).body expect(us.first.username).to eq('sferik') expect(us.last.username).to eq('pengwynn') end it "handles nested arrays of hashes" do body = [[{ "username" => "sferik" }, { "username" => "pengwynn" }]] us = process(body).body.first expect(us.first.username).to eq('sferik') expect(us.last.username).to eq('pengwynn') end it "handles mixed arrays" do body = [123, { "username" => "sferik" }, 456] values = process(body).body expect(values.first).to eq(123) expect(values.last).to eq(456) expect(values[1].username).to eq('sferik') end it "allows for use of custom Mash subclasses at the class level" do original_class = described_class.mash_class described_class.mash_class = MyMash begin me = process({}).body expect(me).to be_instance_of(MyMash) ensure described_class.mash_class = original_class end end end context "custom mash subclass", :type => :response do let(:options) { {:mash_class => MyMash} } it "instance level" do me = process({}).body expect(me).to be_instance_of(MyMash) end end end faraday_middleware-0.12.2/spec/unit/method_override_spec.rb000066400000000000000000000043711314064102000240230ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/request/method_override' describe FaradayMiddleware::MethodOverride do let(:middleware) { described_class.new(lambda {|env| env }, *options) } let(:env) { middleware.call faraday_env(request_env(request_method)) } def request_env(method) { :method => method, :request_headers => Faraday::Utils::Headers.new } end shared_examples "overrides method" do |method| it "sets physical method to POST" do expect(env[:method]).to eq(:post) end it "sets header to #{method}" do expect(env[:request_headers]['X-Http-Method-Override']).to eq(method) end end shared_examples "doesn't override method" do |method| it "keeps original method" do expect(env[:method]).to eq(method) end it "doesn't set header value" do expect(env[:request_headers]).not_to have_key('X-Http-Method-Override') end end context "with default options" do let(:options) { nil } context "GET" do let(:request_method) { :get } include_examples "doesn't override method", :get end context "POST" do let(:request_method) { :post } include_examples "doesn't override method", :post end context "PUT" do let(:request_method) { :put } include_examples "overrides method", 'PUT' end end context "configured to rewrite [:patch, :delete]" do let(:options) { [{ :rewrite => [:patch, :delete] }] } context "PUT" do let(:request_method) { :put } include_examples "doesn't override method", :put end context "PATCH" do let(:request_method) { :patch } include_examples "overrides method", 'PATCH' end context "DELETE" do let(:request_method) { :delete } include_examples "overrides method", 'DELETE' end end context "configured to rewrite ['PATCH']" do let(:options) { [{ :rewrite => %w[PATCH] }] } context "PATCH" do let(:request_method) { :patch } include_examples "overrides method", 'PATCH' end end context "with invalid option" do let(:options) { [{ :hello => 'world' }] } let(:request_method) { :get } it "raises key error" do expect{ env }.to raise_error(IndexError, /key [\s\w]*not found/) end end end faraday_middleware-0.12.2/spec/unit/oauth2_spec.rb000066400000000000000000000113301314064102000220370ustar00rootroot00000000000000require 'helper' require 'uri' require 'faraday_middleware/request/oauth2' require 'faraday/utils' describe FaradayMiddleware::OAuth2 do def query_params(env) Faraday::Utils.parse_query env[:url].query end def auth_header(env) env[:request_headers]['Authorization'] end def perform(params = {}, headers = {}) env = { :url => URI('http://example.com/?' + Faraday::Utils.build_query(params)), :request_headers => Faraday::Utils::Headers.new.update(headers) } app = make_app app.call(faraday_env(env)) end def make_app described_class.new(lambda{|env| env}, *Array(options)) end context "no token configured" do let(:options) { [nil, {:token_type => :param}] } it "doesn't add params" do request = perform(:q => 'hello') expect(query_params(request)).to eq('q' => 'hello') end it "doesn't add headers" do expect(auth_header(perform)).to be_nil end it "creates header for explicit token" do request = perform(:q => 'hello', :access_token => 'abc123') expect(query_params(request)).to eq('q' => 'hello', 'access_token' => 'abc123') expect(auth_header(request)).to eq(%(Token token="abc123")) end context "bearer token type configured" do let(:options) { [nil, {:token_type => :bearer}] } it "doesn't add headers" do expect(auth_header(perform)).to be_nil end it "adds header for explicit token" do request = perform(:q => 'hello', :access_token => 'abc123') expect(auth_header(request)).to eq(%(Bearer abc123)) end end end context "default token configured" do let(:options) { ['XYZ', {:token_type => :param}] } it "adds token param" do expect(query_params(perform(:q => 'hello'))).to eq('q' => 'hello', 'access_token' => 'XYZ') end it "adds token header" do expect(auth_header(perform)).to eq(%(Token token="XYZ")) end it "overrides default with explicit token" do request = perform(:q => 'hello', :access_token => 'abc123') expect(query_params(request)).to eq('q' => 'hello', 'access_token' => 'abc123') expect(auth_header(request)).to eq(%(Token token="abc123")) end it "clears default with empty explicit token" do request = perform(:q => 'hello', :access_token => nil) expect(query_params(request).fetch('access_token')).to_not eq('XYZ') expect(auth_header(request)).to be_nil end context "bearer token type configured" do let(:options) { ['XYZ', {:token_type => :bearer}] } it "adds token header" do expect(auth_header(perform)).to eq(%(Bearer XYZ)) end it "overrides default with explicit token" do request = perform(:q => 'hello', :access_token => 'abc123') expect(auth_header(request)).to eq(%(Bearer abc123)) end it "clears default with empty explicit token" do request = perform(:q => 'hello', :access_token => nil) expect(auth_header(request)).to be_nil end end end context "existing Authorization header" do let(:options) { ['XYZ', {:token_type => :param}] } subject { perform({:q => 'hello'}, 'Authorization' => 'custom') } it "adds token param" do expect(query_params(subject)).to eq('q' => 'hello', 'access_token' => 'XYZ') end it "doesn't override existing header" do expect(auth_header(subject)).to eq('custom') end context "bearer token type configured" do let(:options) { ['XYZ', {:token_type => :bearer}] } subject { perform({:q => 'hello'}, 'Authorization' => 'custom') } it "doesn't override existing header" do expect(auth_header(subject)).to eq('custom') end end end context "custom param name configured" do let(:options) { ['XYZ', {:token_type => :param, :param_name => :oauth}] } it "adds token param" do expect(query_params(perform)).to eq('oauth' => 'XYZ') end it "overrides default with explicit token" do request = perform(:oauth => 'abc123') expect(query_params(request)).to eq('oauth' => 'abc123') expect(auth_header(request)).to eq(%(Token token="abc123")) end end context "options without token configuration" do let(:options) { [{:token_type => :param, :param_name => :oauth}] } it "doesn't add param" do expect(query_params(perform)).to be_empty end it "overrides default with explicit token" do expect(query_params(perform(:oauth => 'abc123'))).to eq('oauth' => 'abc123') end end context "invalid param name configured" do let(:options) { ['XYZ', {:token_type => :param, :param_name => nil}] } it "raises error" do expect{ make_app }.to raise_error(ArgumentError, ":param_name can't be blank") end end end faraday_middleware-0.12.2/spec/unit/oauth_spec.rb000066400000000000000000000113731314064102000217640ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/request/oauth' require 'uri' require 'forwardable' describe FaradayMiddleware::OAuth do def auth_header(env) env[:request_headers]['Authorization'] end def auth_values(env) if auth = auth_header(env) raise "invalid header: #{auth.inspect}" unless auth.sub!('OAuth ', '') Hash[*auth.split(/, |=/)] end end def perform(oauth_options = {}, headers = {}, params = {}) env = { :url => URI('http://example.com/'), :request_headers => Faraday::Utils::Headers.new.update(headers), :request => {}, :body => params } unless oauth_options.is_a? Hash and oauth_options.empty? env[:request][:oauth] = oauth_options end app = make_app app.call(faraday_env(env)) end def make_app described_class.new(lambda{|env| env}, *Array(options)) end context "invalid options" do let(:options) { nil } it "errors out" do expect{ make_app }.to raise_error(ArgumentError) end end context "empty options" do let(:options) { [{}] } it "signs request" do auth = auth_values(perform) expected_keys = %w[ oauth_nonce oauth_signature oauth_signature_method oauth_timestamp oauth_version ] expect(auth.keys).to match_array expected_keys end end context "configured with consumer and token" do let(:options) do [{ :consumer_key => 'CKEY', :consumer_secret => 'CSECRET', :token => 'TOKEN', :token_secret => 'TSECRET' }] end it "adds auth info to the header" do auth = auth_values(perform) expected_keys = %w[ oauth_consumer_key oauth_nonce oauth_signature oauth_signature_method oauth_timestamp oauth_token oauth_version ] expect(auth.keys).to match_array expected_keys expect(auth['oauth_version']).to eq(%("1.0")) expect(auth['oauth_signature_method']).to eq(%("HMAC-SHA1")) expect(auth['oauth_consumer_key']).to eq(%("CKEY")) expect(auth['oauth_token']).to eq(%("TOKEN")) end it "doesn't override existing header" do request = perform({}, "Authorization" => "iz me!") expect(auth_header(request)).to eq("iz me!") end it "can override oauth options per-request" do auth = auth_values(perform(:consumer_key => 'CKEY2')) expect(auth['oauth_consumer_key']).to eq(%("CKEY2")) expect(auth['oauth_token']).to eq(%("TOKEN")) end it "can turn off oauth signing per-request" do expect(auth_header(perform(false))).to be_nil end end context "configured without token" do let(:options) { [{ :consumer_key => 'CKEY', :consumer_secret => 'CSECRET' }] } it "adds auth info to the header" do auth = auth_values(perform) expect(auth).to include('oauth_consumer_key') expect(auth).not_to include('oauth_token') end end context "handling body parameters" do let(:options) { [{ :consumer_key => 'CKEY', :consumer_secret => 'CSECRET', :nonce => '547fed103e122eecf84c080843eedfe6', :timestamp => '1286830180'}] } let(:value) { {'foo' => 'bar'} } let(:type_json) { {'Content-Type' => 'application/json'} } let(:type_form) { {'Content-Type' => 'application/x-www-form-urlencoded'} } extend Forwardable query_method = :build_nested_query query_module = ::Faraday::Utils.respond_to?(query_method) ? 'Faraday::Utils' : 'Rack::Utils' def_delegator query_module, query_method it "does not include the body for JSON" do auth_header_with = auth_header(perform({}, type_json, '{"foo":"bar"}')) auth_header_without = auth_header(perform({}, type_json, {})) expect(auth_header_with).to eq(auth_header_without) end it "includes the body parameters with form Content-Type" do auth_header_with = auth_header(perform({}, type_form, {})) auth_header_without = auth_header(perform({}, type_form, value)) expect(auth_header_with).not_to eq(auth_header_without) end it "includes the body parameters with an unspecified Content-Type" do auth_header_with = auth_header(perform({}, {}, value)) auth_header_without = auth_header(perform({}, type_form, value)) expect(auth_header_with).to eq(auth_header_without) end it "includes the body parameters for form type with string body" do # simulates the behavior of Faraday::MiddleWare::UrlEncoded value = { 'foo' => ['bar', 'baz', 'wat'] } auth_header_hash = auth_header(perform({}, type_form, value)) auth_header_string = auth_header(perform({}, type_form, build_nested_query(value))) expect(auth_header_string).to eq(auth_header_hash) end end end faraday_middleware-0.12.2/spec/unit/parse_dates_spec.rb000066400000000000000000000033071314064102000231340ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/parse_dates' require 'json' describe FaradayMiddleware::ParseDates, :type => :response do let(:parsed){ if RUBY_VERSION > "1.9" "2012-02-01 13:14:15 UTC" else "Wed Feb 01 13:14:15 UTC 2012" end } it "parses dates" do expect(process({"x" => "2012-02-01T13:14:15Z"}).body["x"].to_s).to eq(parsed) expect(process({"x" => "2012-02-01T15:14:15+02:00"}).body["x"].utc.to_s).to eq(parsed) expect(process({"x" => "2012-02-01T11:14:15-0200"}).body["x"].utc.to_s).to eq(parsed) end it "parses dates with milliseconds" do date_str = "2012-02-01T13:14:15.123Z" non_zulu_date_str = "2012-02-01T11:14:15.123-02:00" expect(process({"x" => date_str}).body["x"]).to eq(Time.parse(date_str)) expect(process({"x" => non_zulu_date_str}).body["x"]).to eq(Time.parse(date_str)) end it "parses nested dates in hash" do expect(process({"x" => {"y" => "2012-02-01T13:14:15Z"}}).body["x"]["y"].to_s).to eq(parsed) end it "parses nested dates in arrays" do expect(process({"x" => [{"y" =>"2012-02-01T13:14:15Z"}]}).body["x"][0]["y"].to_s).to eq(parsed) end it "returns nil when body is empty" do expect(process(nil).body).to eq(nil) end it "leaves arrays with ids alone" do expect(process({"x" => [1,2,3]}).body).to eq({"x" => [1,2,3]}) end it "does not parse date-like things" do expect(process({"x" => "2012-02-01T13:14:15Z bla"}).body["x"].to_s).to eq "2012-02-01T13:14:15Z bla" expect(process({"x" => "12012-02-01T13:14:15Z"}).body["x"].to_s).to eq "12012-02-01T13:14:15Z" expect(process({"x" => "2012-02-01T13:14:15Z\nfoo"}).body["x"].to_s).to eq "2012-02-01T13:14:15Z\nfoo" end end faraday_middleware-0.12.2/spec/unit/parse_json_spec.rb000066400000000000000000000077631314064102000230170ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/parse_json' describe FaradayMiddleware::ParseJson, :type => :response do context "no type matching" do it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "nullifies empty body" do expect(process('').body).to be_nil end it "parses json body" do response = process('{"a":1}') expect(response.body).to eq('a' => 1) expect(response.env[:raw_body]).to be_nil end end context "with preserving raw" do let(:options) { {:preserve_raw => true} } it "parses json body" do response = process('{"a":1}') expect(response.body).to eq('a' => 1) expect(response.env[:raw_body]).to eq('{"a":1}') end it "can opt out of preserving raw" do response = process('{"a":1}', nil, :preserve_raw => false) expect(response.env[:raw_body]).to be_nil end end context "with regexp type matching" do let(:options) { {:content_type => /\bjson$/} } it "parses json body of correct type" do response = process('{"a":1}', 'application/x-json') expect(response.body).to eq('a' => 1) end it "ignores json body of incorrect type" do response = process('{"a":1}', 'text/json-xml') expect(response.body).to eq('{"a":1}') end end context "with array type matching" do let(:options) { {:content_type => %w[a/b c/d]} } it "parses json body of correct type" do expect(process('{"a":1}', 'a/b').body).to be_a(Hash) expect(process('{"a":1}', 'c/d').body).to be_a(Hash) end it "ignores json body of incorrect type" do expect(process('{"a":1}', 'a/d').body).not_to be_a(Hash) end end it "chokes on invalid json" do ['{!', '"a"', 'true', 'null', '1'].each do |data| expect{ process(data) }.to raise_error(Faraday::Error::ParsingError) end end it "includes the response on the ParsingError instance" do begin process('{') { |env| env[:response] = Faraday::Response.new } fail 'Parsing should have failed.' rescue Faraday::Error::ParsingError => err expect(err.response).to be_a(Faraday::Response) end end context "with mime type fix" do let(:middleware) { app = described_class::MimeTypeFix.new(lambda {|env| Faraday::Response.new(env) }, :content_type => /^text\//) described_class.new(app, :content_type => 'application/json') } it "ignores completely incompatible type" do response = process('{"a":1}', 'application/xml') expect(response.body).to eq('{"a":1}') end it "ignores compatible type with bad data" do response = process('var a = 1', 'text/javascript') expect(response.body).to eq('var a = 1') expect(response['content-type']).to eq('text/javascript') end it "corrects compatible type and data" do response = process('{"a":1}', 'text/javascript') expect(response.body).to be_a(Hash) expect(response['content-type']).to eq('application/json') end it "corrects compatible type even when data starts with whitespace" do response = process(%( \r\n\t{"a":1}), 'text/javascript') expect(response.body).to be_a(Hash) expect(response['content-type']).to eq('application/json') end end context "HEAD responses" do it "nullifies the body if it's only one space" do response = process(' ') expect(response.body).to be_nil end it "nullifies the body if it's two spaces" do response = process(' ') expect(response.body).to be_nil end end context "JSON options" do let(:body) { '{"a": 1}' } let(:result) { {a: 1} } let(:options) do { :parser_options => { :symbolize_names => true } } end it "passes relevant options to JSON parse" do expect(::JSON).to receive(:parse) .with(body, options[:parser_options]) .and_return(result) response = process(body) expect(response.body).to eq(result) end end end faraday_middleware-0.12.2/spec/unit/parse_marshal_spec.rb000066400000000000000000000006741314064102000234670ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/parse_marshal' describe FaradayMiddleware::ParseMarshal, :type => :response do it "restores a marshaled dump" do expect(process(Marshal.dump(:a => 1)).body).to be_eql(:a => 1) end it "nulifies blank response" do expect(process('').body).to be_nil end it "chokes on invalid content" do expect{ process('abc') }.to raise_error(Faraday::Error::ParsingError) end end faraday_middleware-0.12.2/spec/unit/parse_xml_spec.rb000066400000000000000000000046411314064102000226360ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/parse_xml' describe FaradayMiddleware::ParseXml, :type => :response do let(:xml) { 'Erik Michaels-Obersferik' } let(:user) { {'user' => {'name' => 'Erik Michaels-Ober', 'screen_name' => 'sferik'} } } context "no type matching" do it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "turns empty body into empty hash" do expect(process('').body).to be_eql({}) end it "parses xml body" do response = process(xml) expect(response.body).to eq(user) expect(response.env[:raw_body]).to be_nil end end context "with preserving raw" do let(:options) { {:preserve_raw => true} } it "parses xml body" do response = process(xml) expect(response.body).to eq(user) expect(response.env[:raw_body]).to eq(xml) end it "can opt out of preserving raw" do response = process(xml, nil, :preserve_raw => false) expect(response.env[:raw_body]).to be_nil end end context "with regexp type matching" do let(:options) { {:content_type => /\bxml$/} } it "parses xml body of correct type" do response = process(xml, 'application/xml') expect(response.body).to eq(user) end it "ignores xml body of incorrect type" do response = process(xml, 'text/html') expect(response.body).to eq(xml) end end context "with array type matching" do let(:options) { {:content_type => %w[a/b c/d]} } it "parses xml body of correct type" do expect(process(xml, 'a/b').body).to be_a(Hash) expect(process(xml, 'c/d').body).to be_a(Hash) end it "ignores xml body of incorrect type" do expect(process(xml, 'a/d').body).not_to be_a(Hash) end end it "chokes on invalid xml" do ['{!', '"a"', 'true', 'null', '1'].each do |data| expect{ process(data) }.to raise_error(Faraday::Error::ParsingError) end end context "MultiXml options" do let(:options) do { :parser_options => { :symbolize_names => true } } end it "passes relevant options to MultiXml parse" do expect(::MultiXml).to receive(:parse) .with(xml, options[:parser_options]) .and_return(user) response = process(xml) expect(response.body).to be(user) end end end faraday_middleware-0.12.2/spec/unit/parse_yaml_spec.rb000066400000000000000000000036321314064102000227770ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/parse_yaml' describe FaradayMiddleware::ParseYaml, :type => :response do context "no type matching" do it "doesn't change nil body" do expect(process(nil).body).to be_nil end it "returns false for empty body" do expect(process('').body).to be false end it "parses yaml body" do response = process('a: 1') expect(response.body).to eq('a' => 1) expect(response.env[:raw_body]).to be_nil end end context "with preserving raw" do let(:options) { {:preserve_raw => true} } it "parses yaml body" do response = process('a: 1') expect(response.body).to eq('a' => 1) expect(response.env[:raw_body]).to eq('a: 1') end it "can opt out of preserving raw" do response = process('a: 1', nil, :preserve_raw => false) expect(response.env[:raw_body]).to be_nil end end context "with regexp type matching" do let(:options) { {:content_type => /\byaml$/} } it "parses json body of correct type" do response = process('a: 1', 'application/x-yaml') expect(response.body).to eq('a' => 1) end it "ignores json body of incorrect type" do response = process('a: 1', 'text/yaml-xml') expect(response.body).to eq('a: 1') end end it "chokes on invalid yaml" do expect{ process('{!') }.to raise_error(Faraday::Error::ParsingError) end context "SafeYAML options" do let(:body) { 'a: 1' } let(:result) { {a: 1} } let(:options) do { :parser_options => { :symbolize_names => true } } end it "passes relevant options to SafeYAML load" do expect(::SafeYAML).to receive(:load) .with(body, nil, options[:parser_options]) .and_return(result) response = process(body) expect(response.body).to be(result) end end end faraday_middleware-0.12.2/spec/unit/rashify_spec.rb000066400000000000000000000026551314064102000223140ustar00rootroot00000000000000require 'helper' require 'faraday_middleware/response/rashify' describe FaradayMiddleware::Rashify do context "when used", :type => :response do it "creates a Hashie::Rash from the body" do body = { "name" => "Erik Michaels-Ober", "username" => "sferik" } me = process(body).body expect(me.name).to eq("Erik Michaels-Ober") expect(me.username).to eq("sferik") end it "handles strings" do body = "Most amazing string EVER" me = process(body).body expect(me).to eq("Most amazing string EVER") end it "handles hashes and decamelcase the keys" do body = { "name" => "Erik Michaels-Ober", "userName" => "sferik" } me = process(body).body expect(me.name).to eq('Erik Michaels-Ober') expect(me.user_name).to eq('sferik') end it "handles arrays" do body = [123, 456] values = process(body).body expect(values).to eq([123, 456]) end it "handles arrays of hashes" do body = [{ "username" => "sferik" }, { "username" => "pengwynn" }] us = process(body).body expect(us.first.username).to eq('sferik') expect(us.last.username).to eq('pengwynn') end it "handles mixed arrays" do body = [123, { "username" => "sferik" }, 456] values = process(body).body expect(values.first).to eq(123) expect(values.last).to eq(456) expect(values[1].username).to eq('sferik') end end end