pax_global_header00006660000000000000000000000064121452221200014501gustar00rootroot0000000000000052 comment=ed600bbd2a229a54965ddb63e09e035e06a7f640 ruby-faraday-0.8.7/000077500000000000000000000000001214522212000141035ustar00rootroot00000000000000ruby-faraday-0.8.7/Gemfile000066400000000000000000000011461214522212000154000ustar00rootroot00000000000000source 'https://rubygems.org' group :development do gem 'sinatra', '~> 1.3' end group :test do gem 'em-http-request', '~> 1.0', :require => 'em-http' gem 'em-synchrony', '~> 1.0', :require => ['em-synchrony', 'em-synchrony/em-http'] gem 'excon', '>= 0.16.1' gem 'net-http-persistent', '~> 2.5', :require => false gem 'leftright', '~> 0.9', :require => false gem 'rack-test', '~> 0.6', :require => 'rack/test' end platforms :ruby do gem 'patron', '~> 0.4', '> 0.4.1' gem 'typhoeus', '~> 0.3.3' end platforms :jruby do gem 'jruby-openssl', '~> 0.7' gem 'ffi-ncurses', '~> 0.3' end gemspec ruby-faraday-0.8.7/LICENSE.md000066400000000000000000000020531214522212000155070ustar00rootroot00000000000000Copyright (c) 2009 rick olson, zack hobson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ruby-faraday-0.8.7/README.md000066400000000000000000000163621214522212000153720ustar00rootroot00000000000000# Faraday Faraday is an HTTP client lib that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle. Faraday supports these adapters: * Net::HTTP * [Excon][] * [Typhoeus][] * [Patron][] * [EventMachine][] It also includes a Rack adapter for hitting loaded Rack applications through Rack::Test, and a Test adapter for stubbing requests by hand. ## Usage ```ruby conn = Faraday.new(:url => 'http://sushi.com') do |faraday| faraday.request :url_encoded # form-encode POST params faraday.response :logger # log requests to STDOUT faraday.adapter Faraday.default_adapter # make requests with Net::HTTP end ## GET ## response = conn.get '/nigiri/sake.json' # GET http://sushi.com/nigiri/sake.json response.body conn.get '/nigiri', { :name => 'Maguro' } # GET /nigiri?name=Maguro conn.get do |req| # GET http://sushi.com/search?page=2&limit=100 req.url '/search', :page => 2 req.params['limit'] = 100 end ## POST ## conn.post '/nigiri', { :name => 'Maguro' } # POST "name=maguro" to http://sushi.com/nigiri # post payload as JSON instead of "www-form-urlencoded" encoding: conn.post do |req| req.url '/nigiri' req.headers['Content-Type'] = 'application/json' req.body = '{ "name": "Unagi" }' end ## Per-request options ## conn.get do |req| req.url '/search' req.options[:timeout] = 5 # open/read timeout in seconds req.options[:open_timeout] = 2 # connection open timeout in seconds end ``` If you don't need to set up anything, you can roll with just the bare minimum: ```ruby # using the default stack: response = Faraday.get 'http://sushi.com/nigiri/sake.json' ``` ## Advanced middleware usage The order in which middleware is stacked is important. Like with Rack, the first middleware on the list wraps all others, while the last middleware is the innermost one, so that must be the adapter. ```ruby Faraday.new(...) do |conn| # POST/PUT params encoders: conn.request :multipart conn.request :url_encoded conn.adapter :net_http end ``` This request middleware setup affects POST/PUT requests in the following way: 1. `Request::Multipart` checks for files in the payload, otherwise leaves everything untouched; 2. `Request::UrlEncoded` encodes as "application/x-www-form-urlencoded" if not already encoded or of another type Swapping middleware means giving the other priority. Specifying the "Content-Type" for the request is explicitly stating which middleware should process it. Examples: ```ruby # uploading a file: payload[:profile_pic] = Faraday::UploadIO.new('/path/to/avatar.jpg', 'image/jpeg') # "Multipart" middleware detects files and encodes with "multipart/form-data": conn.put '/profile', payload ``` ## Writing middleware Middleware are classes that implement a `call` instance method. They hook into the request/response cycle. ```ruby def call(env) # do something with the request @app.call(env).on_complete do # do something with the response end end ``` It's important to do all processing of the response only in the `on_complete` block. This enables middleware to work in parallel mode where requests are asynchronous. The `env` is a hash with symbol keys that contains info about the request and, later, response. Some keys are: ``` # request phase :method - :get, :post, ... :url - URI for the current request; also contains GET parameters :body - POST parameters for :post/:put requests :request_headers # response phase :status - HTTP response status code, such as 200 :body - the response body :response_headers ``` ## Using Faraday for testing ```ruby # It's possible to define stubbed request outside a test adapter block. stubs = Faraday::Adapter::Test::Stubs.new do |stub| stub.get('/tamago') { [200, {}, 'egg'] } end # You can pass stubbed request to the test adapter or define them in a block # or a combination of the two. test = Faraday.new do |builder| builder.adapter :test, stubs do |stub| stub.get('/ebi') {[ 200, {}, 'shrimp' ]} end end # It's also possible to stub additional requests after the connection has # been initialized. This is useful for testing. stubs.get('/uni') {[ 200, {}, 'urchin' ]} resp = test.get '/tamago' resp.body # => 'egg' resp = test.get '/ebi' resp.body # => 'shrimp' resp = test.get '/uni' resp.body # => 'urchin' resp = test.get '/else' #=> raises "no such stub" error # If you like, you can treat your stubs as mocks by verifying that all of # the stubbed calls were made. NOTE that this feature is still fairly # experimental: It will not verify the order or count of any stub, only that # it was called once during the course of the test. stubs.verify_stubbed_calls ``` ## TODO * support streaming requests/responses * better stubbing API ## Contributing You can run the test suite against a live server by running `script/test`. It automatically starts a test server in background. Only tests in `test/adapters/*_test.rb` require a server, though. ``` sh # run the whole suite $ script/test # run only specific files $ script/test excon typhoeus ``` We will accept middleware that: 1. is useful to a broader audience, but can be implemented relatively simple; and 2. which isn't already present in [faraday_middleware][] project. We will accept adapters that: 1. support SSL & streaming; 1. are proven and may have better performance than existing ones; or 2. if they have features not present in included adapters. We are pushing towards a 1.0 release, when we will have to follow [Semantic Versioning][semver]. If your patch includes changes to break compatiblitity, note that so we can add it to the [Changelog][]. ## Supported Ruby versions This library aims to support and is [tested against][travis] the following Ruby implementations: * MRI 1.8.7 * MRI 1.9.2 * MRI 1.9.3 * [JRuby][] * [Rubinius][] If something doesn't work on one of these interpreters, it should be considered a bug. This library may inadvertently work (or seem to work) on other Ruby implementations, however support will only be provided for the versions listed above. If you would like this library to support another Ruby version, you may volunteer to be a maintainer. Being a maintainer entails making sure all tests run and pass on that implementation. When something breaks on your implementation, you will be personally responsible for providing patches in a timely fashion. If critical issues for a particular implementation exist at the time of a major release, support for that Ruby version may be dropped. ## Copyright Copyright (c) 2009-2012 [Rick Olson](mailto:technoweenie@gmail.com), zack hobson. See [LICENSE][] for details. [license]: https://github.com/technoweenie/faraday/blob/master/LICENSE.md [travis]: http://travis-ci.org/technoweenie/faraday [jruby]: http://jruby.org/ [rubinius]: http://rubini.us/ [semver]: http://semver.org/ [changelog]: https://github.com/technoweenie/faraday/wiki/Changelog [excon]: https://github.com/geemus/excon#readme [typhoeus]: https://github.com/typhoeus/typhoeus#readme [patron]: http://toland.github.com/patron/ [eventmachine]: https://github.com/igrigorik/em-http-request#readme [faraday_middleware]: https://github.com/pengwynn/faraday_middleware/wiki ruby-faraday-0.8.7/Rakefile000066400000000000000000000041121214522212000155460ustar00rootroot00000000000000require 'date' require 'fileutils' require 'openssl' require 'rake/testtask' task :default => :test ## helper functions def name @name ||= Dir['*.gemspec'].first.split('.').first end def version line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/] line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1] end def gemspec_file "#{name}.gemspec" end def gem_file "#{name}-#{version}.gem" end def replace_header(head, header_name) head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"} end # Adapted from WEBrick::Utils. Skips cert extensions so it # can be used as a CA bundle def create_self_signed_cert(bits, cn, comment) rsa = OpenSSL::PKey::RSA.new(bits) cert = OpenSSL::X509::Certificate.new cert.version = 2 cert.serial = 1 name = OpenSSL::X509::Name.new(cn) cert.subject = name cert.issuer = name cert.not_before = Time.now cert.not_after = Time.now + (365*24*60*60) cert.public_key = rsa.public_key cert.sign(rsa, OpenSSL::Digest::SHA1.new) return [cert, rsa] end ## standard tasks desc "Run all tests" task :test do exec 'script/test' end desc "Generate certificates for SSL tests" task :'test:generate_certs' do cert, key = create_self_signed_cert(1024, [['CN', 'localhost']], 'Faraday Test CA') FileUtils.mkdir_p 'tmp' File.open('tmp/faraday-cert.key', 'w') {|f| f.puts(key) } File.open('tmp/faraday-cert.crt', 'w') {|f| f.puts(cert.to_s) } end file 'tmp/faraday-cert.key' => :'test:generate_certs' file 'tmp/faraday-cert.crt' => :'test:generate_certs' desc "Open an irb session preloaded with this library" task :console do sh "irb -rubygems -r ./lib/#{name}.rb" end ## release management tasks desc "Commit, create tag v#{version} and build and push #{gem_file} to Rubygems" task :release => :build do sh "git commit --allow-empty -a -m 'Release #{version}'" sh "git tag v#{version}" sh "git push origin" sh "git push origin v#{version}" sh "gem push pkg/#{gem_file}" end desc "Build #{gem_file} into the pkg directory" task :build do FileUtils.mkdir_p 'pkg' sh "gem build #{gemspec_file}" sh "mv #{gem_file} pkg" end ruby-faraday-0.8.7/lib/000077500000000000000000000000001214522212000146515ustar00rootroot00000000000000ruby-faraday-0.8.7/lib/faraday.rb000066400000000000000000000105101214522212000166020ustar00rootroot00000000000000module Faraday VERSION = "0.8.7" class << self attr_accessor :root_path, :lib_path attr_accessor :default_adapter attr_writer :default_connection def new(url = nil, options = {}) block = block_given? ? Proc.new : nil Faraday::Connection.new(url, options, &block) end def require_libs(*libs) libs.each do |lib| require "#{lib_path}/#{lib}" end end alias require_lib require_libs private def method_missing(name, *args, &block) default_connection.send(name, *args, &block) end end self.root_path = File.expand_path "..", __FILE__ self.lib_path = File.expand_path "../faraday", __FILE__ self.default_adapter = :net_http def self.default_connection @default_connection ||= Connection.new end if (!defined?(RUBY_ENGINE) || "ruby" == RUBY_ENGINE) && RUBY_VERSION < '1.9' begin require 'system_timer' Timer = SystemTimer rescue LoadError warn "Faraday: you may want to install system_timer for reliable timeouts" end end unless const_defined? :Timer require 'timeout' Timer = Timeout end module MiddlewareRegistry # Internal: Register middleware class(es) on the current module. # # mapping - A Hash mapping Symbol keys to classes. See # Faraday.register_middleware for more details. def register_middleware(mapping) (@registered_middleware ||= {}).update(mapping) end # Internal: Lookup middleware class with a registered Symbol shortcut. # # Returns a middleware Class. def lookup_middleware(key) unless defined? @registered_middleware and found = @registered_middleware[key] raise "#{key.inspect} is not registered on #{self}" end found = @registered_middleware[key] = found.call if found.is_a? Proc found.is_a?(Module) ? found : const_get(found) end end module AutoloadHelper def autoload_all(prefix, options) if prefix =~ /^faraday(\/|$)/i prefix = File.join(Faraday.root_path, prefix) end options.each do |const_name, path| autoload const_name, File.join(prefix, path) end end # Loads each autoloaded constant. If thread safety is a concern, wrap # this in a Mutex. def load_autoloaded_constants constants.each do |const| const_get(const) if autoload?(const) end end def all_loaded_constants constants.map { |c| const_get(c) }. select { |a| a.respond_to?(:loaded?) && a.loaded? } end end extend AutoloadHelper # Public: register middleware classes under a short name. # # type - A Symbol specifying the kind of middleware (default: :middleware) # mapping - A Hash mapping Symbol keys to classes. Classes can be expressed # as fully qualified constant, or a Proc that will be lazily called # to return the former. # # Examples # # Faraday.register_middleware :aloha => MyModule::Aloha # Faraday.register_middleware :response, :boom => MyModule::Boom # # # shortcuts are now available in Builder: # builder.use :aloha # builder.response :boom # # Returns nothing. def self.register_middleware type, mapping = nil type, mapping = :middleware, type if mapping.nil? component = self.const_get(type.to_s.capitalize) component.register_middleware(mapping) end autoload_all "faraday", :Middleware => 'middleware', :Builder => 'builder', :Request => 'request', :Response => 'response', :CompositeReadIO => 'upload_io', :UploadIO => 'upload_io', :Parts => 'upload_io' require_libs "utils", "connection", "adapter", "error" end # not pulling in active-support JUST for this method. And I love this method. class Object # Yields x to the block, and then returns x. # The primary purpose of this method is to "tap into" a method chain, # in order to perform operations on intermediate results within the chain. # # (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a. # tap { |x| puts "array: #{x.inspect}" }. # select { |x| x%2 == 0 }. # tap { |x| puts "evens: #{x.inspect}" }. # map { |x| x*x }. # tap { |x| puts "squares: #{x.inspect}" } def tap yield self self end unless Object.respond_to?(:tap) end ruby-faraday-0.8.7/lib/faraday/000077500000000000000000000000001214522212000162605ustar00rootroot00000000000000ruby-faraday-0.8.7/lib/faraday/adapter.rb000066400000000000000000000035051214522212000202300ustar00rootroot00000000000000module Faraday class Adapter < Middleware CONTENT_LENGTH = 'Content-Length'.freeze extend AutoloadHelper extend MiddlewareRegistry autoload_all 'faraday/adapter', :NetHttp => 'net_http', :NetHttpPersistent => 'net_http_persistent', :Typhoeus => 'typhoeus', :EMSynchrony => 'em_synchrony', :EMHttp => 'em_http', :Patron => 'patron', :Excon => 'excon', :Test => 'test', :Rack => 'rack' register_middleware \ :test => :Test, :net_http => :NetHttp, :net_http_persistent => :NetHttpPersistent, :typhoeus => :Typhoeus, :patron => :Patron, :em_synchrony => :EMSynchrony, :em_http => :EMHttp, :excon => :Excon, :rack => :Rack module Parallelism attr_writer :supports_parallel def supports_parallel?() @supports_parallel end def inherited(subclass) super subclass.supports_parallel = self.supports_parallel? end end extend Parallelism self.supports_parallel = false def call(env) if !env[:body] and Connection::METHODS_WITH_BODIES.include? env[:method] # play nice and indicate we're sending an empty body env[:request_headers][CONTENT_LENGTH] = "0" # Typhoeus hangs on PUT requests if body is nil env[:body] = '' end end def save_response(env, status, body, headers = nil) env[:status] = status env[:body] = body env[:response_headers] = Utils::Headers.new.tap do |response_headers| response_headers.update headers unless headers.nil? yield response_headers if block_given? end end end end ruby-faraday-0.8.7/lib/faraday/adapter/000077500000000000000000000000001214522212000177005ustar00rootroot00000000000000ruby-faraday-0.8.7/lib/faraday/adapter/em_http.rb000066400000000000000000000135111214522212000216660ustar00rootroot00000000000000module Faraday class Adapter # EventMachine adapter is useful for either asynchronous requests # when in EM reactor loop or for making parallel requests in # synchronous code. class EMHttp < Faraday::Adapter module Options def connection_config(env) options = {} configure_ssl(options, env) configure_proxy(options, env) configure_timeout(options, env) options end def request_config(env) options = { :body => read_body(env), :head => env[:request_headers], # :keepalive => true, # :file => 'path/to/file', # stream data off disk } configure_compression(options, env) # configure_proxy_auth # :proxy => {:authorization => [user, pass]} # proxy[:username] && proxy[:password] options end def read_body(env) body = env[:body] body.respond_to?(:read) ? body.read : body end def configure_ssl(options, env) if ssl = env[:ssl] # :ssl => { # :private_key_file => '/tmp/server.key', # :cert_chain_file => '/tmp/server.crt', # :verify_peer => false end end def configure_proxy(options, env) if proxy = request_options(env)[:proxy] options[:proxy] = { :host => proxy[:uri].host, :port => proxy[:uri].port } end end def configure_timeout(options, env) timeout, open_timeout = request_options(env).values_at(:timeout, :open_timeout) options[:connect_timeout] = options[:inactivity_timeout] = timeout options[:connect_timeout] = open_timeout if open_timeout end def configure_compression(options, env) if env[:method] == :get and not options[:head].key? 'accept-encoding' options[:head]['accept-encoding'] = 'gzip, compressed' end end def request_options(env) env[:request] end end include Options dependency 'em-http' self.supports_parallel = true def self.setup_parallel_manager(options = nil) Manager.new end def call(env) super perform_request env @app.call env end def perform_request(env) if parallel?(env) manager = env[:parallel_manager] manager.add { perform_single_request(env). callback { env[:response].finish(env) } } else unless EventMachine.reactor_running? error = nil # start EM, block until request is completed EventMachine.run do perform_single_request(env). callback { EventMachine.stop }. errback { |client| error = error_message(client) EventMachine.stop } end raise_error(error) if error else # EM is running: instruct upstream that this is an async request env[:parallel_manager] = true perform_single_request(env). callback { env[:response].finish(env) }. errback { # TODO: no way to communicate the error in async mode raise NotImplementedError } end end end # TODO: reuse the connection to support pipelining def perform_single_request(env) req = EventMachine::HttpRequest.new(env[:url], connection_config(env)) req.setup_request(env[:method], request_config(env)).callback { |client| save_response(env, client.response_header.status, client.response) do |resp_headers| client.response_header.each do |name, value| resp_headers[name.to_sym] = value end end } end def error_message(client) client.error or "request failed" end def raise_error(msg) errklass = Faraday::Error::ClientError if msg == Errno::ETIMEDOUT errklass = Faraday::Error::TimeoutError msg = "request timed out" elsif msg == Errno::ECONNREFUSED errklass = Faraday::Error::ConnectionFailed msg = "connection refused" end raise errklass, msg end def parallel?(env) !!env[:parallel_manager] end # The parallel manager is designed to start an EventMachine loop # and block until all registered requests have been completed. class Manager def initialize reset end def reset @registered_procs = [] @num_registered = 0 @num_succeeded = 0 @errors = [] @running = false end def running?() @running end def add if running? perform_request { yield } else @registered_procs << Proc.new end @num_registered += 1 end def run if @num_registered > 0 @running = true EventMachine.run do @registered_procs.each do |proc| perform_request(&proc) end end if @errors.size > 0 raise Faraday::Error::ClientError, @errors.first || "connection failed" end end ensure reset end def perform_request client = yield client.callback { @num_succeeded += 1; check_finished } client.errback { @errors << client.error; check_finished } end def check_finished if @num_succeeded + @errors.size == @num_registered EventMachine.stop end end end end end end ruby-faraday-0.8.7/lib/faraday/adapter/em_synchrony.rb000066400000000000000000000046671214522212000227570ustar00rootroot00000000000000require 'uri' module Faraday class Adapter class EMSynchrony < Faraday::Adapter include EMHttp::Options dependency do require 'em-synchrony/em-http' require 'em-synchrony/em-multi' require 'fiber' end self.supports_parallel = true def self.setup_parallel_manager(options = {}) ParallelManager.new end def call(env) super request = EventMachine::HttpRequest.new(URI::parse(env[:url].to_s), connection_config(env)) # end http_method = env[:method].to_s.downcase.to_sym # Queue requests for parallel execution. if env[:parallel_manager] env[:parallel_manager].add(request, http_method, request_config(env)) do |resp| save_response(env, resp.response_header.status, resp.response) do |resp_headers| resp.response_header.each do |name, value| resp_headers[name.to_sym] = value end end # Finalize the response object with values from `env`. env[:response].finish(env) end # Execute single request. else client = nil block = lambda { request.send(http_method, request_config(env)) } if !EM.reactor_running? EM.run do Fiber.new { client = block.call EM.stop }.resume end else client = block.call end save_response(env, client.response_header.status, client.response) do |resp_headers| client.response_header.each do |name, value| resp_headers[name.to_sym] = value end end end @app.call env rescue Errno::ECONNREFUSED raise Error::ConnectionFailed, $! end end end end require 'faraday/adapter/em_synchrony/parallel_manager' # add missing patch(), options() methods EventMachine::HTTPMethods.module_eval do [:patch, :options].each do |type| next if method_defined? :"a#{type}" alias_method :"a#{type}", type if method_defined? type module_eval %[ def #{type}(options = {}, &blk) f = Fiber.current conn = setup_request(:#{type}, options, &blk) if conn.error.nil? conn.callback { f.resume(conn) } conn.errback { f.resume(conn) } Fiber.yield else conn end end ] end end ruby-faraday-0.8.7/lib/faraday/adapter/em_synchrony/000077500000000000000000000000001214522212000224155ustar00rootroot00000000000000ruby-faraday-0.8.7/lib/faraday/adapter/em_synchrony/parallel_manager.rb000066400000000000000000000031271214522212000262330ustar00rootroot00000000000000module Faraday class Adapter class EMSynchrony < Faraday::Adapter class ParallelManager # Add requests to queue. The `request` argument should be a # `EM::HttpRequest` object. def add(request, method, *args, &block) queue << { :request => request, :method => method, :args => args, :block => block } end # Run all requests on queue with `EM::Synchrony::Multi`, wrapping # it in a reactor and fiber if needed. def run result = nil if !EM.reactor_running? EM.run { Fiber.new do result = perform EM.stop end.resume } else result = perform end result end private # The request queue. def queue @queue ||= [] end # Main `EM::Synchrony::Multi` performer. def perform multi = ::EM::Synchrony::Multi.new queue.each do |item| method = "a#{item[:method]}".to_sym req = item[:request].send(method, *item[:args]) req.callback(&item[:block]) req_name = "req_#{multi.requests.size}".to_sym multi.add(req_name, req) end # Clear the queue, so parallel manager objects can be reused. @queue = [] # Block fiber until all requests have returned. multi.perform end end # ParallelManager end # EMSynchrony end # Adapter end # Faraday ruby-faraday-0.8.7/lib/faraday/adapter/excon.rb000066400000000000000000000032731214522212000213460ustar00rootroot00000000000000module Faraday class Adapter class Excon < Faraday::Adapter dependency 'excon' def call(env) super opts = {} if env[:url].scheme == 'https' && ssl = env[:ssl] opts[:ssl_verify_peer] = !!ssl.fetch(:verify, true) opts[:ssl_ca_path] = ssl[:ca_path] if ssl[:ca_path] opts[:ssl_ca_file] = ssl[:ca_file] if ssl[:ca_file] # https://github.com/geemus/excon/issues/106 # https://github.com/jruby/jruby-ossl/issues/19 opts[:nonblock] = false end if ( req = env[:request] ) if req[:timeout] opts[:read_timeout] = req[:timeout] opts[:connect_timeout] = req[:timeout] opts[:write_timeout] = req[:timeout] end if req[:open_timeout] opts[:connect_timeout] = req[:open_timeout] opts[:write_timeout] = req[:open_timeout] end end conn = ::Excon.new(env[:url].to_s, opts) resp = conn.request \ :method => env[:method].to_s.upcase, :headers => env[:request_headers], :body => read_body(env) save_response(env, resp.status.to_i, resp.body, resp.headers) @app.call env rescue ::Excon::Errors::SocketError => err if err.message =~ /\btimeout\b/ raise Error::TimeoutError, err else raise Error::ConnectionFailed, err end rescue ::Excon::Errors::Timeout => err raise Error::TimeoutError, err end # TODO: support streaming requests def read_body(env) env[:body].respond_to?(:read) ? env[:body].read : env[:body] end end end end ruby-faraday-0.8.7/lib/faraday/adapter/net_http.rb000066400000000000000000000072151214522212000220570ustar00rootroot00000000000000begin require 'net/https' rescue LoadError warn "Warning: no such file to load -- net/https. Make sure openssl is installed if you want ssl support" require 'net/http' end require 'zlib' module Faraday class Adapter class NetHttp < Faraday::Adapter NET_HTTP_EXCEPTIONS = [ EOFError, Errno::ECONNABORTED, Errno::ECONNREFUSED, Errno::ENETUNREACH, Errno::ECONNRESET, Errno::EINVAL, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, SocketError, Zlib::GzipFile::Error, ] NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL) def call(env) super http = net_http_connection(env) configure_ssl(http, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl] req = env[:request] http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout] http.open_timeout = req[:open_timeout] if req[:open_timeout] begin http_response = perform_request(http, env) rescue *NET_HTTP_EXCEPTIONS raise Error::ConnectionFailed, $! end save_response(env, http_response.code.to_i, http_response.body) do |response_headers| http_response.each_header do |key, value| response_headers[key] = value end end @app.call env rescue Timeout::Error => err raise Faraday::Error::TimeoutError, err end def create_request(env) request = Net::HTTPGenericRequest.new \ env[:method].to_s.upcase, # request method !!env[:body], # is there request body :head != env[:method], # is there response body env[:url].request_uri, # request uri path env[:request_headers] # request headers if env[:body].respond_to?(:read) request.body_stream = env[:body] else request.body = env[:body] end request end def perform_request(http, env) if :get == env[:method] and !env[:body] # prefer `get` to `request` because the former handles gzip (ruby 1.9) http.get env[:url].request_uri, env[:request_headers] else http.request create_request(env) end end def net_http_connection(env) if proxy = env[:request][:proxy] Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password]) else Net::HTTP end.new(env[:url].host, env[:url].port) end def configure_ssl(http, ssl) http.use_ssl = true http.verify_mode = ssl_verify_mode(ssl) http.cert_store = ssl_cert_store(ssl) http.cert = ssl[:client_cert] if ssl[:client_cert] http.key = ssl[:client_key] if ssl[:client_key] http.ca_file = ssl[:ca_file] if ssl[:ca_file] http.ca_path = ssl[:ca_path] if ssl[:ca_path] http.verify_depth = ssl[:verify_depth] if ssl[:verify_depth] http.ssl_version = ssl[:version] if ssl[:version] end def ssl_cert_store(ssl) return ssl[:cert_store] if ssl[:cert_store] # Use the default cert store by default, i.e. system ca certs cert_store = OpenSSL::X509::Store.new cert_store.set_default_paths cert_store end def ssl_verify_mode(ssl) ssl[:verify_mode] || begin if ssl.fetch(:verify, true) OpenSSL::SSL::VERIFY_PEER else OpenSSL::SSL::VERIFY_NONE end end end end end end ruby-faraday-0.8.7/lib/faraday/adapter/net_http_persistent.rb000066400000000000000000000023141214522212000243320ustar00rootroot00000000000000require 'faraday/adapter/net_http' module Faraday class Adapter # Experimental adapter for net-http-persistent class NetHttpPersistent < NetHttp dependency 'net/http/persistent' # TODO: investigate is it safe to create a new Persistent instance for # every request, or does it defy the purpose of persistent connections def net_http_connection(env) Net::HTTP::Persistent.new 'Faraday', env[:request][:proxy] ? env[:request][:proxy][:uri] : nil end def perform_request(http, env) http.request env[:url], create_request(env) rescue Net::HTTP::Persistent::Error => error if error.message.include? 'Timeout::Error' raise Faraday::Error::TimeoutError, error else raise end end def configure_ssl(http, ssl) http.verify_mode = ssl_verify_mode(ssl) http.cert_store = ssl_cert_store(ssl) http.certificate = ssl[:client_cert] if ssl[:client_cert] http.private_key = ssl[:client_key] if ssl[:client_key] http.ca_file = ssl[:ca_file] if ssl[:ca_file] http.ssl_version = ssl[:version] if ssl[:version] end end end end ruby-faraday-0.8.7/lib/faraday/adapter/patron.rb000066400000000000000000000037061214522212000215360ustar00rootroot00000000000000module Faraday class Adapter class Patron < Faraday::Adapter dependency 'patron' def initialize(app, &block) super(app) @block = block if block_given? end def call(env) super # TODO: support streaming requests env[:body] = env[:body].read if env[:body].respond_to? :read session = @session ||= create_session if req = env[:request] session.timeout = session.connect_timeout = req[:timeout] if req[:timeout] session.connect_timeout = req[:open_timeout] if req[:open_timeout] if proxy = req[:proxy] session.proxy = proxy[:uri].to_s if proxy[:user] && proxy[:password] prepend_proxy_auth_string(proxy, session) end end end response = begin data = env[:body] ? env[:body].to_s : nil session.request(env[:method], env[:url].to_s, env[:request_headers], :data => data) rescue Errno::ECONNREFUSED raise Error::ConnectionFailed, $! end save_response(env, response.status, response.body, response.headers) @app.call env rescue ::Patron::TimeoutError => err raise Faraday::Error::TimeoutError, err end if loaded? && defined?(::Patron::Request::VALID_ACTIONS) # HAX: helps but doesn't work completely # https://github.com/toland/patron/issues/34 ::Patron::Request::VALID_ACTIONS.tap do |actions| actions << :patch unless actions.include? :patch actions << :options unless actions.include? :options end end def create_session session = ::Patron::Session.new session.insecure = true @block.call(session) if @block session end end def prepend_proxy_auth_string(proxy, session) session.proxy.insert(7, "#{proxy[:user]}:#{proxy[:password]}@") end end end ruby-faraday-0.8.7/lib/faraday/adapter/rack.rb000066400000000000000000000032101214522212000211410ustar00rootroot00000000000000require 'timeout' module Faraday class Adapter # Sends requests to a Rack app. # # Examples # # class MyRackApp # def call(env) # [200, {'Content-Type' => 'text/html'}, ["hello world"]] # end # end # # Faraday.new do |conn| # conn.adapter :rack, MyRackApp.new # end class Rack < Faraday::Adapter dependency 'rack/test' # not prefixed with "HTTP_" SPECIAL_HEADERS = %w[ CONTENT_LENGTH CONTENT_TYPE ] def initialize(faraday_app, rack_app) super(faraday_app) mock_session = ::Rack::MockSession.new(rack_app) @session = ::Rack::Test::Session.new(mock_session) end def call(env) super rack_env = { :method => env[:method], :input => env[:body].respond_to?(:read) ? env[:body].read : env[:body], 'rack.url_scheme' => env[:url].scheme } env[:request_headers].each do |name, value| name = name.upcase.tr('-', '_') name = "HTTP_#{name}" unless SPECIAL_HEADERS.include? name rack_env[name] = value end if env[:request_headers] timeout = env[:request][:timeout] || env[:request][:open_timeout] response = if timeout Timer.timeout(timeout, Faraday::Error::TimeoutError) { execute_request(env, rack_env) } else execute_request(env, rack_env) end save_response(env, response.status, response.body, response.headers) @app.call env end def execute_request(env, rack_env) @session.request(env[:url].to_s, rack_env) end end end end ruby-faraday-0.8.7/lib/faraday/adapter/test.rb000066400000000000000000000104521214522212000212060ustar00rootroot00000000000000module Faraday class Adapter # test = Faraday::Connection.new do # use Faraday::Adapter::Test do |stub| # stub.get '/nigiri/sake.json' do # [200, {}, 'hi world'] # end # end # end # # resp = test.get '/nigiri/sake.json' # resp.body # => 'hi world' # class Test < Faraday::Adapter attr_accessor :stubs class Stubs class NotFound < StandardError end def initialize # {:get => [Stub, Stub]} @stack, @consumed = {}, {} yield self if block_given? end def empty? @stack.empty? end def match(request_method, path, body) return false if !@stack.key?(request_method) stack = @stack[request_method] consumed = (@consumed[request_method] ||= []) path = normalize_path(path) if stub = matches?(stack, path, body) consumed << stack.delete(stub) stub else matches?(consumed, path, body) end end def get(path, &block) new_stub(:get, path, &block) end def head(path, &block) new_stub(:head, path, &block) end def post(path, body=nil, &block) new_stub(:post, path, body, &block) end def put(path, body=nil, &block) new_stub(:put, path, body, &block) end def patch(path, body=nil, &block) new_stub(:patch, path, body, &block) end def delete(path, &block) new_stub(:delete, path, &block) end def options(path, &block) new_stub(:options, path, &block) end # Raises an error if any of the stubbed calls have not been made. def verify_stubbed_calls failed_stubs = [] @stack.each do |method, stubs| unless stubs.size == 0 failed_stubs.concat(stubs.map {|stub| "Expected #{method} #{stub}." }) end end raise failed_stubs.join(" ") unless failed_stubs.size == 0 end protected def new_stub(request_method, path, body=nil, &block) (@stack[request_method] ||= []) << Stub.new(normalize_path(path), body, block) end def matches?(stack, path, body) stack.detect { |stub| stub.matches?(path, body) } end # ensure leading + trailing slash def normalize_path(path) path = '/' + path if path.index('/') != 0 path = path.sub('?', '/?') path = path + '/' unless $& path.gsub('//', '/') end end class Stub < Struct.new(:path, :params, :body, :block) def initialize(full, body, block) path, query = full.split('?') params = query ? Faraday::Utils.parse_nested_query(query) : {} super path, params, body, block end def matches?(request_uri, request_body) request_path, request_query = request_uri.split('?') request_params = request_query ? Faraday::Utils.parse_nested_query(request_query) : {} request_path == path && params_match?(request_params) && (body.to_s.size.zero? || request_body == body) end def params_match?(request_params) params.keys.all? do |key| request_params[key] == params[key] end end def to_s "#{path} #{body}" end end def initialize(app, stubs=nil, &block) super(app) @stubs = stubs || Stubs.new configure(&block) if block end def configure yield stubs end def call(env) super normalized_path = Faraday::Utils.normalize_path(env[:url]) if stub = stubs.match(env[:method], normalized_path, env[:body]) env[:params] = (query = env[:url].query) ? Faraday::Utils.parse_nested_query(query) : {} status, headers, body = stub.block.call(env) save_response(env, status, body, headers) else raise Stubs::NotFound, "no stubbed request for #{env[:method]} #{normalized_path} #{env[:body]}" end @app.call(env) end end end end ruby-faraday-0.8.7/lib/faraday/adapter/typhoeus.rb000066400000000000000000000055721214522212000221160ustar00rootroot00000000000000module Faraday class Adapter class Typhoeus < Faraday::Adapter self.supports_parallel = true def self.setup_parallel_manager(options = {}) options.empty? ? ::Typhoeus::Hydra.hydra : ::Typhoeus::Hydra.new(options) end dependency 'typhoeus' def call(env) super perform_request env @app.call env end def perform_request(env) read_body env hydra = env[:parallel_manager] || self.class.setup_parallel_manager hydra.queue request(env) hydra.run unless parallel?(env) rescue Errno::ECONNREFUSED raise Error::ConnectionFailed, $! end # TODO: support streaming requests def read_body(env) env[:body] = env[:body].read if env[:body].respond_to? :read end def request(env) req = ::Typhoeus::Request.new env[:url].to_s, :method => env[:method], :body => env[:body], :headers => env[:request_headers], :disable_ssl_peer_verification => (env[:ssl] && !env[:ssl].fetch(:verify, true)) configure_ssl req, env configure_proxy req, env configure_timeout req, env req.on_complete do |resp| if resp.timed_out? if parallel?(env) # TODO: error callback in async mode else raise Faraday::Error::TimeoutError, "request timed out" end end save_response(env, resp.code, resp.body) do |response_headers| response_headers.parse resp.headers end # in async mode, :response is initialized at this point env[:response].finish(env) if parallel?(env) end req end def configure_ssl(req, env) ssl = env[:ssl] req.ssl_version = ssl[:version] if ssl[:version] req.ssl_cert = ssl[:client_cert_file] if ssl[:client_cert_file] req.ssl_key = ssl[:client_key_file] if ssl[:client_key_file] req.ssl_cacert = ssl[:ca_file] if ssl[:ca_file] req.ssl_capath = ssl[:ca_path] if ssl[:ca_path] end def configure_proxy(req, env) proxy = request_options(env)[:proxy] return unless proxy req.proxy = "#{proxy[:uri].host}:#{proxy[:uri].port}" if proxy[:username] && proxy[:password] req.proxy_username = proxy[:username] req.proxy_password = proxy[:password] end end def configure_timeout(req, env) env_req = request_options(env) req.timeout = req.connect_timeout = (env_req[:timeout] * 1000) if env_req[:timeout] req.connect_timeout = (env_req[:open_timeout] * 1000) if env_req[:open_timeout] end def request_options(env) env[:request] end def parallel?(env) !!env[:parallel_manager] end end end end ruby-faraday-0.8.7/lib/faraday/builder.rb000066400000000000000000000074721214522212000202450ustar00rootroot00000000000000module Faraday # Possibly going to extend this a bit. # # Faraday::Connection.new(:url => 'http://sushi.com') do |builder| # builder.request :url_encoded # Faraday::Request::UrlEncoded # builder.adapter :net_http # Faraday::Adapter::NetHttp # end class Builder attr_accessor :handlers # Error raised when trying to modify the stack after calling `lock!` class StackLocked < RuntimeError; end # borrowed from ActiveSupport::Dependencies::Reference & # ActionDispatch::MiddlewareStack::Middleware class Handler @@constants = Hash.new { |h, k| h[k] = k.respond_to?(:constantize) ? k.constantize : Object.const_get(k) } attr_reader :name def initialize(klass, *args, &block) @name = klass.to_s @@constants[@name] = klass if klass.respond_to?(:name) @args, @block = args, block end def klass() @@constants[@name] end def inspect() @name end def ==(other) if other.is_a? Handler self.name == other.name elsif other.respond_to? :name klass == other else @name == other.to_s end end def build(app) klass.new(app, *@args, &@block) end end def initialize(handlers = []) @handlers = handlers if block_given? build(&Proc.new) elsif @handlers.empty? # default stack, if nothing else is configured self.request :url_encoded self.adapter Faraday.default_adapter end end def build(options = {}) raise_if_locked @handlers.clear unless options[:keep] yield self if block_given? end def [](idx) @handlers[idx] end def ==(other) other.is_a?(self.class) && @handlers == other.handlers end def dup self.class.new(@handlers.dup) end def to_app(inner_app) # last added handler is the deepest and thus closest to the inner app @handlers.reverse.inject(inner_app) { |app, handler| handler.build(app) } end # Locks the middleware stack to ensure no further modifications are possible. def lock! @handlers.freeze end def locked? @handlers.frozen? end def use(klass, *args, &block) if klass.is_a? Symbol use_symbol(Faraday::Middleware, klass, *args, &block) else raise_if_locked @handlers << self.class::Handler.new(klass, *args, &block) end end def request(key, *args, &block) use_symbol(Faraday::Request, key, *args, &block) end def response(key, *args, &block) use_symbol(Faraday::Response, key, *args, &block) end def adapter(key, *args, &block) use_symbol(Faraday::Adapter, key, *args, &block) end ## methods to push onto the various positions in the stack: def insert(index, *args, &block) raise_if_locked index = assert_index(index) handler = self.class::Handler.new(*args, &block) @handlers.insert(index, handler) end alias_method :insert_before, :insert def insert_after(index, *args, &block) index = assert_index(index) insert(index + 1, *args, &block) end def swap(index, *args, &block) raise_if_locked index = assert_index(index) @handlers.delete_at(index) insert(index, *args, &block) end def delete(handler) raise_if_locked @handlers.delete(handler) end private def raise_if_locked raise StackLocked, "can't modify middleware stack after making a request" if locked? end def use_symbol(mod, key, *args, &block) use(mod.lookup_middleware(key), *args, &block) end def assert_index(index) idx = index.is_a?(Integer) ? index : @handlers.index(index) raise "No such handler: #{index.inspect}" unless idx idx end end end ruby-faraday-0.8.7/lib/faraday/connection.rb000066400000000000000000000234031214522212000207460ustar00rootroot00000000000000require 'cgi' require 'set' require 'forwardable' require 'uri' Faraday.require_libs 'builder', 'request', 'response', 'utils' module Faraday class Connection METHODS = Set.new [:get, :post, :put, :delete, :head, :patch, :options] METHODS_WITH_BODIES = Set.new [:post, :put, :patch, :options] attr_reader :params, :headers, :url_prefix, :builder, :options, :ssl, :parallel_manager attr_writer :default_parallel_manager # Public: Initializes a new Faraday::Connection. # # url - URI or String base URL to use as a prefix for all # requests (optional). # options - Hash of settings that will be applied to every request made # from this Connection (default: {}). # :url - URI or String base URL (default: "http:/"). # :params - Hash of URI query unencoded key/value pairs. # :headers - Hash of unencoded HTTP header key/value pairs. # :request - Hash of request options. # :ssl - Hash of SSL options. # :proxy - URI, String or Hash of HTTP proxy options # (default: "http_proxy" environment variable). # :uri - URI or String # :user - String (optional) # :password - String (optional) def initialize(url = nil, options = {}) if url.is_a?(Hash) options = url url = options[:url] end @headers = Utils::Headers.new @params = Utils::ParamsHash.new @options = options[:request] || {} @ssl = options[:ssl] || {} @parallel_manager = nil @default_parallel_manager = options[:parallel_manager] @builder = options[:builder] || begin # pass an empty block to Builder so it doesn't assume default middleware block = block_given?? Proc.new {|b| } : nil Builder.new(&block) end self.url_prefix = url || 'http:/' @params.update options[:params] if options[:params] @headers.update options[:headers] if options[:headers] @proxy = nil proxy(options.fetch(:proxy) { ENV['http_proxy'] }) yield self if block_given? @headers[:user_agent] ||= "Faraday v#{VERSION}" end # Public: Replace default query parameters. def params=(hash) @params.replace hash end # Public: Replace default request headers. def headers=(hash) @headers.replace hash end extend Forwardable def_delegators :builder, :build, :use, :request, :response, :adapter # The "rack app" wrapped in middleware. All requests are sent here. # # The builder is responsible for creating the app object. After this, # the builder gets locked to ensure no further modifications are made # to the middleware stack. # # Returns an object that responds to `call` and returns a Response. def app @app ||= begin builder.lock! builder.to_app(lambda { |env| # the inner app that creates and returns the Response object response = Response.new response.finish(env) unless env[:parallel_manager] env[:response] = response }) end end # get/head/delete(url, params, headers) %w[get head delete].each do |method| class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{method}(url = nil, params = nil, headers = nil) run_request(:#{method}, url, nil, headers) { |request| request.params.update(params) if params yield request if block_given? } end RUBY end # post/put/patch(url, body, headers) %w[post put patch].each do |method| class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{method}(url = nil, body = nil, headers = nil, &block) run_request(:#{method}, url, body, headers, &block) end RUBY end def basic_auth(login, pass) headers[Faraday::Request::Authorization::KEY] = Faraday::Request::BasicAuthentication.header(login, pass) end def token_auth(token, options = nil) headers[Faraday::Request::Authorization::KEY] = Faraday::Request::TokenAuthentication.header(token, options) end def authorization(type, token) headers[Faraday::Request::Authorization::KEY] = Faraday::Request::Authorization.header(type, token) end # Internal: Traverse the middleware stack in search of a # parallel-capable adapter. # # Yields in case of not found. # # Returns a parallel manager or nil if not found. def default_parallel_manager @default_parallel_manager ||= begin handler = @builder.handlers.detect do |h| h.klass.respond_to?(:supports_parallel?) and h.klass.supports_parallel? end if handler then handler.klass.setup_parallel_manager elsif block_given? then yield end end end def in_parallel? !!@parallel_manager end def in_parallel(manager = nil) @parallel_manager = manager || default_parallel_manager { warn "Warning: `in_parallel` called but no parallel-capable adapter on Faraday stack" warn caller[2,10].join("\n") nil } yield @parallel_manager && @parallel_manager.run ensure @parallel_manager = nil end def proxy(arg = nil) return @proxy if arg.nil? @proxy = if arg.is_a? Hash uri = self.class.URI arg.fetch(:uri) { raise ArgumentError, "missing :uri" } arg.merge :uri => uri else uri = self.class.URI(arg) {:uri => uri} end with_uri_credentials(uri) do |user, password| @proxy[:user] ||= user @proxy[:password] ||= password end @proxy end # normalize URI() behavior across Ruby versions def self.URI(url) if url.respond_to?(:host) url elsif url.respond_to?(:to_str) Kernel.URI(url) else raise ArgumentError, "bad argument (expected URI object or URI string)" end end def_delegators :url_prefix, :scheme, :scheme=, :host, :host=, :port, :port= def_delegator :url_prefix, :path, :path_prefix # Parses the giving url with URI and stores the individual # components in this connection. These components serve as defaults for # requests made by this connection. # # conn = Faraday::Connection.new { ... } # conn.url_prefix = "https://sushi.com/api" # conn.scheme # => https # conn.path_prefix # => "/api" # # conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri # def url_prefix=(url) uri = @url_prefix = self.class.URI(url) self.path_prefix = uri.path params.merge_query(uri.query) uri.query = nil with_uri_credentials(uri) do |user, password| basic_auth user, password uri.user = uri.password = nil end uri end # Ensures that the path prefix always has a leading but no trailing slash def path_prefix=(value) url_prefix.path = if value value = value.chomp '/' value = '/' + value unless value[0,1] == '/' value end end def run_request(method, url, body, headers) if !METHODS.include?(method) raise ArgumentError, "unknown http method: #{method}" end request = build_request(method) do |req| req.url(url) if url req.headers.update(headers) if headers req.body = body if body yield req if block_given? end env = request.to_env(self) self.app.call(env) end # Internal: Creates and configures the request object. # # Returns the new Request. def build_request(method) Request.create(method) do |req| req.params = self.params.dup req.headers = self.headers.dup req.options = self.options.merge(:proxy => self.proxy) yield req if block_given? end end # Takes a relative url for a request and combines it with the defaults # set on the connection instance. # # conn = Faraday::Connection.new { ... } # conn.url_prefix = "https://sushi.com/api?token=abc" # conn.scheme # => https # conn.path_prefix # => "/api" # # conn.build_url("nigiri?page=2") # => https://sushi.com/api/nigiri?token=abc&page=2 # conn.build_url("nigiri", :page => 2) # => https://sushi.com/api/nigiri?token=abc&page=2 # def build_url(url, extra_params = nil) uri = build_exclusive_url(url) query_values = self.params.dup.merge_query(uri.query) query_values.update extra_params if extra_params uri.query = query_values.empty? ? nil : query_values.to_query uri end # Internal: Build an absolute URL based on url_prefix. # # url - A String or URI-like object # params - A Faraday::Utils::ParamsHash to replace the query values # of the resulting url (default: nil). # # Returns the resulting URI instance. def build_exclusive_url(url, params = nil) url = nil if url.respond_to?(:empty?) and url.empty? base = url_prefix if url and base.path and base.path !~ /\/$/ base = base.dup base.path = base.path + '/' # ensure trailing slash end uri = url ? base + url : base uri.query = params.to_query if params uri.query = nil if uri.query and uri.query.empty? uri end def dup self.class.new(build_url(''), :headers => headers.dup, :params => params.dup, :builder => builder.dup, :ssl => ssl.dup) end # Internal: Yields username and password extracted from a URI if they both exist. def with_uri_credentials(uri) if uri.user and uri.password yield Utils.unescape(uri.user), Utils.unescape(uri.password) end end end end ruby-faraday-0.8.7/lib/faraday/error.rb000066400000000000000000000016611214522212000177420ustar00rootroot00000000000000module Faraday module Error class ClientError < StandardError attr_reader :response def initialize(ex, response = nil) @wrapped_exception = nil @response = response if ex.respond_to?(:backtrace) super(ex.message) @wrapped_exception = ex elsif ex.respond_to?(:each_key) super("the server responded with status #{ex[:status]}") @response = ex else super(ex.to_s) end end def backtrace if @wrapped_exception @wrapped_exception.backtrace else super end end def inspect %(#<#{self.class}>) end end class ConnectionFailed < ClientError; end class ResourceNotFound < ClientError; end class ParsingError < ClientError; end class TimeoutError < ClientError; end class MissingDependency < StandardError; end end end ruby-faraday-0.8.7/lib/faraday/middleware.rb000066400000000000000000000013651214522212000207270ustar00rootroot00000000000000module Faraday class Middleware extend MiddlewareRegistry class << self attr_accessor :load_error private :load_error= end self.load_error = nil # Executes a block which should try to require and reference dependent libraries def self.dependency(lib = nil) lib ? require(lib) : yield rescue LoadError, NameError => error self.load_error = error end def self.new(*) raise "missing dependency for #{self}: #{load_error.message}" unless loaded? super end def self.loaded? load_error.nil? end def self.inherited(subclass) super subclass.send(:load_error=, self.load_error) end def initialize(app = nil) @app = app end end end ruby-faraday-0.8.7/lib/faraday/request.rb000066400000000000000000000062251214522212000203020ustar00rootroot00000000000000module Faraday # Used to setup urls, params, headers, and the request body in a sane manner. # # @connection.post do |req| # req.url 'http://localhost', 'a' => '1' # 'http://localhost?a=1' # req.headers['b'] = '2' # Header # req.params['c'] = '3' # GET Param # req['b'] = '2' # also Header # req.body = 'abc' # end # class Request < Struct.new(:method, :path, :params, :headers, :body, :options) extend AutoloadHelper extend MiddlewareRegistry autoload_all 'faraday/request', :UrlEncoded => 'url_encoded', :Multipart => 'multipart', :Retry => 'retry', :Timeout => 'timeout', :Authorization => 'authorization', :BasicAuthentication => 'basic_authentication', :TokenAuthentication => 'token_authentication' register_middleware \ :url_encoded => :UrlEncoded, :multipart => :Multipart, :retry => :Retry, :authorization => :Authorization, :basic_auth => :BasicAuthentication, :token_auth => :TokenAuthentication def self.create(request_method) new(request_method).tap do |request| yield request if block_given? end end # Public: Replace params, preserving the existing hash type def params=(hash) if params then params.replace hash else super end end # Public: Replace request headers, preserving the existing hash type def headers=(hash) if headers then headers.replace hash else super end end def url(path, params = nil) if path.respond_to? :query if query = path.query path = path.dup path.query = nil end else path, query = path.split('?', 2) end self.path = path self.params.merge_query query self.params.update(params) if params end def [](key) headers[key] end def []=(key, value) headers[key] = value end # ENV Keys # :method - a symbolized request method (:get, :post) # :body - the request body that will eventually be converted to a string. # :url - URI instance for the current request. # :status - HTTP response status code # :request_headers - hash of HTTP Headers to be sent to the server # :response_headers - Hash of HTTP headers from the server # :parallel_manager - sent if the connection is in parallel mode # :request - Hash of options for configuring the request. # :timeout - open/read timeout Integer in seconds # :open_timeout - read timeout Integer in seconds # :proxy - Hash of proxy options # :uri - Proxy Server URI # :user - Proxy server username # :password - Proxy server password # :ssl - Hash of options for configuring SSL requests. def to_env(connection) { :method => method, :body => body, :url => connection.build_exclusive_url(path, params), :request_headers => headers, :parallel_manager => connection.parallel_manager, :request => options, :ssl => connection.ssl} end end end ruby-faraday-0.8.7/lib/faraday/request/000077500000000000000000000000001214522212000177505ustar00rootroot00000000000000ruby-faraday-0.8.7/lib/faraday/request/authorization.rb000066400000000000000000000016761214522212000232070ustar00rootroot00000000000000module Faraday class Request::Authorization < Faraday::Middleware KEY = "Authorization".freeze # Public def self.header(type, token) case token when String, Symbol then "#{type} #{token}" when Hash then build_hash(type.to_s, token) else raise ArgumentError, "Can't build an Authorization #{type} header from #{token.inspect}" end end # Internal def self.build_hash(type, hash) offset = KEY.size + type.size + 3 comma = ",\n#{' ' * offset}" values = [] hash.each do |key, value| values << "#{key}=#{value.to_s.inspect}" end "#{type} #{values * comma}" end def initialize(app, type, token) @header_value = self.class.header(type, token) super(app) end # Public def call(env) unless env[:request_headers][KEY] env[:request_headers][KEY] = @header_value end @app.call(env) end end end ruby-faraday-0.8.7/lib/faraday/request/basic_authentication.rb000066400000000000000000000004161214522212000244560ustar00rootroot00000000000000require 'base64' module Faraday class Request::BasicAuthentication < Request::Authorization # Public def self.header(login, pass) value = Base64.encode64([login, pass].join(':')) value.gsub!("\n", '') super(:Basic, value) end end end ruby-faraday-0.8.7/lib/faraday/request/multipart.rb000066400000000000000000000036321214522212000223220ustar00rootroot00000000000000module Faraday class Request::Multipart < Request::UrlEncoded self.mime_type = 'multipart/form-data'.freeze DEFAULT_BOUNDARY = "-----------RubyMultipartPost".freeze def call(env) match_content_type(env) do |params| env[:request] ||= {} env[:request][:boundary] ||= DEFAULT_BOUNDARY env[:request_headers][CONTENT_TYPE] += ";boundary=#{env[:request][:boundary]}" env[:body] = create_multipart(env, params) end @app.call env end def process_request?(env) type = request_type(env) env[:body].respond_to?(:each_key) and !env[:body].empty? and ( (type.empty? and has_multipart?(env[:body])) or type == self.class.mime_type ) end def has_multipart?(obj) # string is an enum in 1.8, returning list of itself if obj.respond_to?(:each) && !obj.is_a?(String) (obj.respond_to?(:values) ? obj.values : obj).each do |val| return true if (val.respond_to?(:content_type) || has_multipart?(val)) end end false end def create_multipart(env, params) boundary = env[:request][:boundary] parts = process_params(params) do |key, value| Faraday::Parts::Part.new(boundary, key, value) end parts << Faraday::Parts::EpiloguePart.new(boundary) body = Faraday::CompositeReadIO.new(parts) env[:request_headers]['Content-Length'] = body.length.to_s return body end def process_params(params, prefix = nil, pieces = nil, &block) params.inject(pieces || []) do |all, (key, value)| key = "#{prefix}[#{key}]" if prefix case value when Array values = value.inject([]) { |a,v| a << [nil, v] } process_params(values, key, all, &block) when Hash process_params(value, key, all, &block) else all << block.call(key, value) end end end end end ruby-faraday-0.8.7/lib/faraday/request/retry.rb000066400000000000000000000006011214522212000214370ustar00rootroot00000000000000module Faraday class Request::Retry < Faraday::Middleware def initialize(app, retries = 2) @retries = retries super(app) end def call(env) retries = @retries begin @app.call(env) rescue StandardError, Timeout::Error if retries > 0 retries -= 1 retry end raise end end end end ruby-faraday-0.8.7/lib/faraday/request/token_authentication.rb000066400000000000000000000004761214522212000245230ustar00rootroot00000000000000module Faraday class Request::TokenAuthentication < Request::Authorization # Public def self.header(token, options = nil) options ||= {} options[:token] = token super :Token, options end def initialize(app, token, options = nil) super(app, token, options) end end end ruby-faraday-0.8.7/lib/faraday/request/url_encoded.rb000066400000000000000000000016101214522212000225560ustar00rootroot00000000000000module Faraday class Request::UrlEncoded < Faraday::Middleware CONTENT_TYPE = 'Content-Type'.freeze class << self attr_accessor :mime_type end self.mime_type = 'application/x-www-form-urlencoded'.freeze def call(env) match_content_type(env) do |data| env[:body] = Faraday::Utils.build_nested_query data end @app.call env end def match_content_type(env) if process_request?(env) env[:request_headers][CONTENT_TYPE] ||= self.class.mime_type yield env[:body] unless env[:body].respond_to?(:to_str) end end def process_request?(env) type = request_type(env) env[:body] and (type.empty? or type == self.class.mime_type) end def request_type(env) type = env[:request_headers][CONTENT_TYPE].to_s type = type.split(';', 2).first if type.index(';') type end end end ruby-faraday-0.8.7/lib/faraday/response.rb000066400000000000000000000043041214522212000204440ustar00rootroot00000000000000require 'forwardable' module Faraday class Response # Used for simple response middleware. class Middleware < Faraday::Middleware def call(env) @app.call(env).on_complete do |environment| on_complete(environment) end end # Override this to modify the environment after the response has finished. # Calls the `parse` method if defined def on_complete(env) if respond_to? :parse env[:body] = parse(env[:body]) unless [204,304].index env[:status] end end end extend Forwardable extend AutoloadHelper extend MiddlewareRegistry autoload_all 'faraday/response', :RaiseError => 'raise_error', :Logger => 'logger' register_middleware \ :raise_error => :RaiseError, :logger => :Logger def initialize(env = nil) @env = env @on_complete_callbacks = [] end attr_reader :env alias_method :to_hash, :env def status finished? ? env[:status] : nil end def headers finished? ? env[:response_headers] : {} end def_delegator :headers, :[] def body finished? ? env[:body] : nil end def finished? !!env end def on_complete if not finished? @on_complete_callbacks << Proc.new else yield env end return self end def finish(env) raise "response already finished" if finished? @env = env @on_complete_callbacks.each { |callback| callback.call(env) } return self end def success? (200..299).include?(status) end # because @on_complete_callbacks cannot be marshalled def marshal_dump !finished? ? nil : { :status => @env[:status], :body => @env[:body], :response_headers => @env[:response_headers] } end def marshal_load(env) @env = env end # Expand the env with more properties, without overriding existing ones. # Useful for applying request params after restoring a marshalled Response. def apply_request(request_env) raise "response didn't finish yet" unless finished? @env = request_env.merge @env return self end end end ruby-faraday-0.8.7/lib/faraday/response/000077500000000000000000000000001214522212000201165ustar00rootroot00000000000000ruby-faraday-0.8.7/lib/faraday/response/logger.rb000066400000000000000000000013541214522212000217250ustar00rootroot00000000000000require 'forwardable' module Faraday class Response::Logger < Response::Middleware extend Forwardable def initialize(app, logger = nil) super(app) @logger = logger || begin require 'logger' ::Logger.new(STDOUT) end end def_delegators :@logger, :debug, :info, :warn, :error, :fatal def call(env) info "#{env[:method]} #{env[:url].to_s}" debug('request') { dump_headers env[:request_headers] } super end def on_complete(env) info('Status') { env[:status].to_s } debug('response') { dump_headers env[:response_headers] } end private def dump_headers(headers) headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n") end end end ruby-faraday-0.8.7/lib/faraday/response/raise_error.rb000066400000000000000000000006701214522212000227620ustar00rootroot00000000000000module Faraday class Response::RaiseError < Response::Middleware def on_complete(env) case env[:status] when 404 raise Faraday::Error::ResourceNotFound, response_values(env) when 400...600 raise Faraday::Error::ClientError, response_values(env) end end def response_values(env) {:status => env[:status], :headers => env[:response_headers], :body => env[:body]} end end end ruby-faraday-0.8.7/lib/faraday/upload_io.rb000066400000000000000000000026001214522212000205560ustar00rootroot00000000000000begin require 'composite_io' require 'parts' require 'stringio' rescue LoadError $stderr.puts "Install the multipart-post gem." raise end module Faraday # Similar but not compatible with ::CompositeReadIO provided by multipart-post. class CompositeReadIO def initialize(*parts) @parts = parts.flatten @ios = @parts.map { |part| part.to_io } @index = 0 end def length @parts.inject(0) { |sum, part| sum + part.length } end def rewind @ios.each { |io| io.rewind } @index = 0 end # Read from IOs in order until `length` bytes have been received. def read(length = nil, outbuf = nil) got_result = false outbuf = outbuf ? outbuf.replace("") : "" while io = current_io if result = io.read(length) got_result ||= !result.nil? result.force_encoding("BINARY") if result.respond_to?(:force_encoding) outbuf << result length -= result.length if length break if length == 0 end advance_io end (!got_result && length) ? nil : outbuf end def close @ios.each { |io| io.close } end def ensure_open_and_readable # Rubinius compatibility end private def current_io @ios[@index] end def advance_io @index += 1 end end UploadIO = ::UploadIO Parts = ::Parts end ruby-faraday-0.8.7/lib/faraday/utils.rb000066400000000000000000000150301214522212000177440ustar00rootroot00000000000000require 'cgi' module Faraday module Utils extend self # Adapted from Rack::Utils::HeaderHash class Headers < ::Hash def initialize(hash={}) super() @names = {} self.update hash end # symbol -> string mapper + cache KeyMap = Hash.new do |map, key| map[key] = if key.respond_to?(:to_str) then key else key.to_s.split('_'). # :user_agent => %w(user agent) each { |w| w.capitalize! }. # => %w(User Agent) join('-') # => "User-Agent" end end KeyMap[:etag] = "ETag" def [](k) k = KeyMap[k] super(k) || super(@names[k.downcase]) end def []=(k, v) k = KeyMap[k] k = (@names[k.downcase] ||= k) # join multiple values with a comma v = v.to_ary.join(', ') if v.respond_to? :to_ary super k, v end def delete(k) k = KeyMap[k] if k = @names[k.downcase] @names.delete k.downcase super k end end def include?(k) @names.include? k.downcase end alias_method :has_key?, :include? alias_method :member?, :include? alias_method :key?, :include? def merge!(other) other.each { |k, v| self[k] = v } self end alias_method :update, :merge! def merge(other) hash = dup hash.merge! other end def replace(other) clear self.update other self end def to_hash() ::Hash.new.update(self) end def parse(header_string) return unless header_string && !header_string.empty? header_string.split(/\r\n/). tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line map { |h| h.split(/:\s+/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines each { |key, value| # join multiple values with a comma if self[key] then self[key] << ', ' << value else self[key] = value end } end end # hash with stringified keys class ParamsHash < Hash def [](key) super(convert_key(key)) end def []=(key, value) super(convert_key(key), value) end def delete(key) super(convert_key(key)) end def include?(key) super(convert_key(key)) end alias_method :has_key?, :include? alias_method :member?, :include? alias_method :key?, :include? def update(params) params.each do |key, value| self[key] = value end self end alias_method :merge!, :update def merge(params) dup.update(params) end def replace(other) clear update(other) end def merge_query(query) if query && !query.empty? update Utils.parse_nested_query(query) end self end def to_query Utils.build_nested_query(self) end private def convert_key(key) key.to_s end end # Copied from Rack def build_query(params) params.map { |k, v| if v.class == Array build_query(v.map { |x| [k, x] }) else v.nil? ? escape(k) : "#{escape(k)}=#{escape(v)}" end }.join("&") end # Rack's version modified to handle non-String values def build_nested_query(value, prefix = nil) case value when Array value.map { |v| build_nested_query(v, "#{prefix}%5B%5D") }.join("&") when Hash value.map { |k, v| build_nested_query(v, prefix ? "#{prefix}%5B#{escape(k)}%5D" : escape(k)) }.join("&") when NilClass prefix else raise ArgumentError, "value must be a Hash" if prefix.nil? "#{prefix}=#{escape(value)}" end end ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/ def escape(s) s.to_s.gsub(ESCAPE_RE) { '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase }.tr(' ', '+') end def unescape(s) CGI.unescape s.to_s end DEFAULT_SEP = /[&;] */n # Adapted from Rack def parse_query(qs) params = {} (qs || '').split(DEFAULT_SEP).each do |p| k, v = p.split('=', 2).map { |x| unescape(x) } if cur = params[k] if cur.class == Array then params[k] << v else params[k] = [cur, v] end else params[k] = v end end params end def parse_nested_query(qs) params = {} (qs || '').split(DEFAULT_SEP).each do |p| k, v = p.split('=', 2).map { |s| unescape(s) } normalize_params(params, k, v) end params end # Stolen from Rack def normalize_params(params, name, v = nil) name =~ %r(\A[\[\]]*([^\[\]]+)\]*) k = $1 || '' after = $' || '' return if k.empty? if after == "" params[k] = v elsif after == "[]" params[k] ||= [] raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) params[k] << v elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) child_key = $1 params[k] ||= [] raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) normalize_params(params[k].last, child_key, v) else params[k] << normalize_params({}, child_key, v) end else params[k] ||= {} raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) params[k] = normalize_params(params[k], after, v) end return params end # Receives a URL and returns just the path with the query string sorted. def normalize_path(url) (url.path != "" ? url.path : "/") + (url.query ? "?#{sort_query_params(url.query)}" : "") end # Recursive hash update def deep_merge!(target, hash) hash.each do |key, value| if Hash === value and Hash === target[key] target[key] = deep_merge(target[key], value) else target[key] = value end end target end # Recursive hash merge def deep_merge(source, hash) deep_merge!(source.dup, hash) end protected def sort_query_params(query) query.split('&').sort.join('&') end end end ruby-faraday-0.8.7/metadata.yml000066400000000000000000000073201214522212000164100ustar00rootroot00000000000000--- !ruby/object:Gem::Specification name: faraday version: !ruby/object:Gem::Version version: 0.8.7 prerelease: platform: ruby authors: - Rick Olson autorequire: bindir: bin cert_chain: [] date: 2013-03-24 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: multipart-post requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '1.1' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '1.1' - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: simplecov requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' description: email: technoweenie@gmail.com executables: [] extensions: [] extra_rdoc_files: [] files: - Gemfile - LICENSE.md - README.md - Rakefile - lib/faraday/adapter/em_http.rb - lib/faraday/adapter/em_synchrony/parallel_manager.rb - lib/faraday/adapter/em_synchrony.rb - lib/faraday/adapter/excon.rb - lib/faraday/adapter/net_http.rb - lib/faraday/adapter/net_http_persistent.rb - lib/faraday/adapter/patron.rb - lib/faraday/adapter/rack.rb - lib/faraday/adapter/test.rb - lib/faraday/adapter/typhoeus.rb - lib/faraday/adapter.rb - lib/faraday/builder.rb - lib/faraday/connection.rb - lib/faraday/error.rb - lib/faraday/middleware.rb - lib/faraday/request/authorization.rb - lib/faraday/request/basic_authentication.rb - lib/faraday/request/multipart.rb - lib/faraday/request/retry.rb - lib/faraday/request/token_authentication.rb - lib/faraday/request/url_encoded.rb - lib/faraday/request.rb - lib/faraday/response/logger.rb - lib/faraday/response/raise_error.rb - lib/faraday/response.rb - lib/faraday/upload_io.rb - lib/faraday/utils.rb - lib/faraday.rb - test/adapters/default_test.rb - test/adapters/em_http_test.rb - test/adapters/em_synchrony_test.rb - test/adapters/excon_test.rb - test/adapters/integration.rb - test/adapters/logger_test.rb - test/adapters/net_http_persistent_test.rb - test/adapters/net_http_test.rb - test/adapters/patron_test.rb - test/adapters/rack_test.rb - test/adapters/test_middleware_test.rb - test/adapters/typhoeus_test.rb - test/authentication_middleware_test.rb - test/composite_read_io_test.rb - test/connection_test.rb - test/env_test.rb - test/helper.rb - test/live_server.rb - test/middleware/retry_test.rb - test/middleware_stack_test.rb - test/request_middleware_test.rb - test/response_middleware_test.rb - script/test homepage: https://github.com/lostisland/faraday licenses: - MIT post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: 1.3.6 requirements: [] rubyforge_project: rubygems_version: 1.8.23 signing_key: specification_version: 2 summary: HTTP/REST API client library. test_files: [] has_rdoc: ruby-faraday-0.8.7/script/000077500000000000000000000000001214522212000154075ustar00rootroot00000000000000ruby-faraday-0.8.7/script/test000077500000000000000000000064621214522212000163240ustar00rootroot00000000000000#!/usr/bin/env ruby # Runs the test suite against a local server spawned automatically in a # thread. After tests are done, the server is shut down. # # If filename arguments are given, only those files are run. If arguments given # are not filenames, they are taken as words that filter the list of files to run. # # Examples # # $ script/test # $ script/test test/env_test.rb # $ script/test excon typhoeus # $ SSL=yes script/test net_http -- -n /ssl/ require 'rubygems' require 'bundler' begin Bundler.setup rescue Bundler::GemNotFound $stderr.print "Error: " $stderr.puts $!.message warn "Run `bundle install` to install missing gems." exit 1 end $VERBOSE = true host = 'localhost' logfile = 'log/test.log' test_glob = 'test/**/*_test.rb' if ssl_mode = ENV['SSL'] == 'yes' unless ENV['SSL_KEY'] and ENV['SSL_FILE'] key_file = ENV['SSL_KEY'] = 'tmp/faraday-cert.key' cert_file = ENV['SSL_FILE'] = 'tmp/faraday-cert.crt' system 'rake', key_file, cert_file abort unless $?.success? end end require 'fileutils' FileUtils.mkdir_p 'log' # find available port require 'socket' port = begin server = TCPServer.new(host, 0) server.addr[1] ensure server.close if server end server = nil Thread.abort_on_exception = true # start test server in a separate thread thread = Thread.new do old_verbose, $VERBOSE = $VERBOSE, nil begin require File.expand_path('../../test/live_server', __FILE__) ensure $VERBOSE = old_verbose end require 'webrick' log_io = File.open logfile, 'w' log_io.sync = true webrick_opts = { :Port => port, :Logger => WEBrick::Log::new(log_io), :AccessLog => [[log_io, "[%{X-Faraday-Adapter}i] %m %U -> %s %b"]] } if ssl_mode require 'webrick/https' webrick_opts.update \ :SSLEnable => true, :SSLPrivateKey => OpenSSL::PKey::RSA.new(File.read(key_file)), :SSLCertificate => OpenSSL::X509::Certificate.new(File.read(cert_file)), :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE end Rack::Handler::WEBrick.run(Faraday::LiveServer, webrick_opts) {|serv| server = serv } end # find files to test test_files = Dir[test_glob] if ARGV.any? all_files, test_files = test_files, [] args, extra_args = ARGV, [] if idx = args.index('--') extra_args = args[(idx+1)..-1] args = args[0, idx] end for arg in args re = /(\b|_)#{arg}(\b|_)/ test_files.concat all_files.select { |f| f =~ re } end test_files.concat extra_args end require 'net/https' conn = Net::HTTP.new host, port conn.open_timeout = conn.read_timeout = 0.1 conn.use_ssl = ssl_mode conn.verify_mode = OpenSSL::SSL::VERIFY_NONE # test if test server is accepting requests responsive = lambda { |path| begin res = conn.start { conn.get(path) } res.is_a?(Net::HTTPSuccess) rescue Errno::ECONNREFUSED, Errno::EBADF, Timeout::Error, Net::HTTPBadResponse false end } server_pings = 0 begin # block until test server is ready thread.join 0.05 server_pings += 1 abort "test server didn't manage to start" if server_pings >= 50 end until responsive.call('/echo') ENV['LIVE'] = "http#{ssl_mode ? 's' : ''}://#{host}:#{port}" ok = system 'ruby', '-Ilib:test', '-e', 'load(ARGV.shift) while ARGV.first =~ /^[^-]/', *test_files server.respond_to?(:stop!) ? server.stop! : server.stop thread.join exit 1 unless ok ruby-faraday-0.8.7/test/000077500000000000000000000000001214522212000150625ustar00rootroot00000000000000ruby-faraday-0.8.7/test/adapters/000077500000000000000000000000001214522212000166655ustar00rootroot00000000000000ruby-faraday-0.8.7/test/adapters/default_test.rb000066400000000000000000000004511214522212000216750ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class DefaultTest < Faraday::TestCase def adapter() :default end Integration.apply(self, :NonParallel) do # default stack is not configured with Multipart undef :test_POST_sends_files end end end ruby-faraday-0.8.7/test/adapters/em_http_test.rb000066400000000000000000000005751214522212000217200ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class EMHttpTest < Faraday::TestCase def adapter() :em_http end Integration.apply(self, :Parallel) do # https://github.com/eventmachine/eventmachine/pull/289 undef :test_timeout end unless jruby? and ssl_mode? # https://github.com/eventmachine/eventmachine/issues/180 end end ruby-faraday-0.8.7/test/adapters/em_synchrony_test.rb000066400000000000000000000005231214522212000227660ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class EMSynchronyTest < Faraday::TestCase def adapter() :em_synchrony end Integration.apply(self, :Parallel) do # https://github.com/eventmachine/eventmachine/pull/289 undef :test_timeout end unless RUBY_VERSION < '1.9' or jruby? end end ruby-faraday-0.8.7/test/adapters/excon_test.rb000066400000000000000000000004461214522212000213710ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class ExconTest < Faraday::TestCase def adapter() :excon end Integration.apply(self, :NonParallel) do # https://github.com/geemus/excon/issues/126 ? undef :test_timeout if ssl_mode? end end end ruby-faraday-0.8.7/test/adapters/integration.rb000066400000000000000000000144511214522212000215420ustar00rootroot00000000000000require 'forwardable' require File.expand_path("../../helper", __FILE__) module Adapters # Adapter integration tests. To use, implement two methods: # # `#adapter` required. returns a symbol for the adapter middleware name # `#adapter_options` optional. extra arguments for building an adapter module Integration def self.apply(base, *extra_features) if base.live_server? features = [:Common] features.concat extra_features features.each {|name| base.send(:include, self.const_get(name)) } yield if block_given? elsif !defined? @warned warn "Warning: Not running integration tests against a live server." warn "Start the server `ruby test/live_server.rb` and set the LIVE=1 env variable." @warned = true end end module Parallel def test_in_parallel resp1, resp2 = nil, nil connection = create_connection connection.in_parallel do resp1 = connection.get('echo?a=1') resp2 = connection.get('echo?b=2') assert connection.in_parallel? assert_nil resp1.body assert_nil resp2.body end assert !connection.in_parallel? assert_equal 'get ?{"a"=>"1"}', resp1.body assert_equal 'get ?{"b"=>"2"}', resp2.body end end module NonParallel def test_no_parallel_support connection = create_connection response = nil err = capture_warnings do connection.in_parallel do response = connection.get('echo').body end end assert response assert_match "no parallel-capable adapter on Faraday stack", err assert_match __FILE__, err end end module Compression def test_GET_handles_compression res = get('echo_header', :name => 'accept-encoding') assert_match /gzip;.+\bdeflate\b/, res.body end end module Common extend Forwardable def_delegators :create_connection, :get, :head, :put, :post, :patch, :delete, :run_request def test_GET_retrieves_the_response_body assert_equal 'get', get('echo').body end def test_GET_send_url_encoded_params assert_equal %(get ?{"name"=>"zack"}), get('echo', :name => 'zack').body end def test_GET_retrieves_the_response_headers response = get('echo') assert_match(/text\/plain/, response.headers['Content-Type'], 'original case fail') assert_match(/text\/plain/, response.headers['content-type'], 'lowercase fail') end def test_GET_handles_headers_with_multiple_values assert_equal 'one, two', get('multi').headers['set-cookie'] end def test_GET_with_body response = get('echo') do |req| req.body = {'bodyrock' => true} end assert_equal %(get {"bodyrock"=>"true"}), response.body end def test_GET_sends_user_agent response = get('echo_header', {:name => 'user-agent'}, :user_agent => 'Agent Faraday') assert_equal 'Agent Faraday', response.body end def test_GET_ssl expected = self.class.ssl_mode?.to_s assert_equal expected, get('ssl').body end def test_POST_send_url_encoded_params assert_equal %(post {"name"=>"zack"}), post('echo', :name => 'zack').body end def test_POST_send_url_encoded_nested_params resp = post('echo', 'name' => {'first' => 'zack'}) assert_equal %(post {"name"=>{"first"=>"zack"}}), resp.body end def test_POST_retrieves_the_response_headers assert_match(/text\/plain/, post('echo').headers['content-type']) end def test_POST_sends_files resp = post('file') do |req| req.body = {'uploaded_file' => Faraday::UploadIO.new(__FILE__, 'text/x-ruby')} end assert_equal "file integration.rb text/x-ruby", resp.body end def test_PUT_send_url_encoded_params assert_equal %(put {"name"=>"zack"}), put('echo', :name => 'zack').body end def test_PUT_send_url_encoded_nested_params resp = put('echo', 'name' => {'first' => 'zack'}) assert_equal %(put {"name"=>{"first"=>"zack"}}), resp.body end def test_PUT_retrieves_the_response_headers assert_match(/text\/plain/, put('echo').headers['content-type']) end def test_PATCH_send_url_encoded_params assert_equal %(patch {"name"=>"zack"}), patch('echo', :name => 'zack').body end def test_OPTIONS resp = run_request(:options, 'echo', nil, {}) assert_equal 'options', resp.body end def test_HEAD_retrieves_no_response_body # FIXME: some adapters return empty string, some nil assert_equal '', head('echo').body.to_s end def test_HEAD_retrieves_the_response_headers assert_match(/text\/plain/, head('echo').headers['content-type']) end def test_DELETE_retrieves_the_response_headers assert_match(/text\/plain/, delete('echo').headers['content-type']) end def test_DELETE_retrieves_the_body assert_equal %(delete), delete('echo').body end def test_timeout conn = create_connection(:request => {:timeout => 1, :open_timeout => 1}) assert_raise Faraday::Error::TimeoutError do conn.get '/slow' end end def adapter raise NotImplementedError.new("Need to override #adapter") end # extra options to pass when building the adapter def adapter_options [] end def create_connection(options = {}) if adapter == :default builder_block = nil else builder_block = Proc.new do |b| b.request :multipart b.request :url_encoded b.adapter adapter, *adapter_options end end server = self.class.live_server url = '%s://%s:%d' % [server.scheme, server.host, server.port] options[:ssl] ||= {} options[:ssl][:ca_file] ||= ENV['SSL_FILE'] Faraday::Connection.new(url, options, &builder_block).tap do |conn| conn.headers['X-Faraday-Adapter'] = adapter.to_s adapter_handler = conn.builder.handlers.last conn.builder.insert_before adapter_handler, Faraday::Response::RaiseError end end end end end ruby-faraday-0.8.7/test/adapters/logger_test.rb000066400000000000000000000016541214522212000215360ustar00rootroot00000000000000require File.expand_path(File.join(File.dirname(__FILE__), '..', 'helper')) require 'stringio' require 'logger' module Adapters class LoggerTest < Faraday::TestCase def setup @io = StringIO.new @logger = Logger.new(@io) @logger.level = Logger::DEBUG @conn = Faraday.new do |b| b.response :logger, @logger b.adapter :test do |stubs| stubs.get('/hello') { [200, {'Content-Type' => 'text/html'}, 'hello'] } end end @resp = @conn.get '/hello', nil, :accept => 'text/html' end def test_still_returns_output assert_equal 'hello', @resp.body end def test_logs_method_and_url assert_match "get http:/hello", @io.string end def test_logs_request_headers assert_match %(Accept: "text/html), @io.string end def test_logs_response_headers assert_match %(Content-Type: "text/html), @io.string end end end ruby-faraday-0.8.7/test/adapters/net_http_persistent_test.rb000066400000000000000000000005001214522212000243510ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class NetHttpPersistentTest < Faraday::TestCase def adapter() :net_http_persistent end Integration.apply(self, :NonParallel) do # https://github.com/drbrain/net-http-persistent/issues/33 undef :test_timeout end end end ruby-faraday-0.8.7/test/adapters/net_http_test.rb000066400000000000000000000004321214522212000220750ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class NetHttpTest < Faraday::TestCase def adapter() :net_http end behaviors = [:NonParallel] behaviors << :Compression if RUBY_VERSION >= '1.9' Integration.apply(self, *behaviors) end end ruby-faraday-0.8.7/test/adapters/patron_test.rb000066400000000000000000000006161214522212000215570ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class Patron < Faraday::TestCase def adapter() :patron end Integration.apply(self, :NonParallel) do # https://github.com/toland/patron/issues/34 undef :test_PATCH_send_url_encoded_params # https://github.com/toland/patron/issues/52 undef :test_GET_with_body end unless jruby? end end ruby-faraday-0.8.7/test/adapters/rack_test.rb000066400000000000000000000012541214522212000211730ustar00rootroot00000000000000require File.expand_path("../integration", __FILE__) require File.expand_path('../../live_server', __FILE__) module Adapters class RackTest < Faraday::TestCase def adapter() :rack end def adapter_options [Faraday::LiveServer] end # no Integration.apply because this doesn't require a server as a separate process include Integration::Common include Integration::NonParallel # not using shared test because error is swallowed by Sinatra def test_timeout conn = create_connection(:request => {:timeout => 1, :open_timeout => 1}) begin conn.get '/slow' rescue Faraday::Error::ClientError end end end end ruby-faraday-0.8.7/test/adapters/test_middleware_test.rb000066400000000000000000000044161214522212000234320ustar00rootroot00000000000000require File.expand_path(File.join(File.dirname(__FILE__), '..', 'helper')) module Adapters class TestMiddleware < Faraday::TestCase def setup @stubs = Faraday::Adapter::Test::Stubs.new @conn = Faraday.new do |builder| builder.adapter :test, @stubs end @stubs.get('/hello') { [200, {'Content-Type' => 'text/html'}, 'hello'] } @resp = @conn.get('/hello') end def test_middleware_with_simple_path_sets_status assert_equal 200, @resp.status end def test_middleware_with_simple_path_sets_headers assert_equal 'text/html', @resp.headers['Content-Type'] end def test_middleware_with_simple_path_sets_body assert_equal 'hello', @resp.body end def test_middleware_can_be_called_several_times assert_equal 'hello', @conn.get("/hello").body end def test_middleware_with_get_params @stubs.get('/param?a=1') { [200, {}, 'a'] } assert_equal 'a', @conn.get('/param?a=1').body end def test_middleware_ignores_unspecified_get_params @stubs.get('/optional?a=1') { [200, {}, 'a'] } assert_equal 'a', @conn.get('/optional?a=1&b=1').body assert_equal 'a', @conn.get('/optional?a=1').body assert_raise Faraday::Adapter::Test::Stubs::NotFound do @conn.get('/optional') end end def test_middleware_allow_different_outcomes_for_the_same_request @stubs.get('/hello') { [200, {'Content-Type' => 'text/html'}, 'hello'] } @stubs.get('/hello') { [200, {'Content-Type' => 'text/html'}, 'world'] } assert_equal 'hello', @conn.get("/hello").body assert_equal 'world', @conn.get("/hello").body end def test_yields_env_to_stubs @stubs.get '/hello' do |env| assert_equal '/hello', env[:url].path assert_equal 'foo.com', env[:url].host assert_equal '1', env[:params]['a'] assert_equal 'text/plain', env[:request_headers]['Accept'] [200, {}, 'a'] end @conn.headers['Accept'] = 'text/plain' assert_equal 'a', @conn.get('http://foo.com/hello?a=1').body end def test_raises_an_error_if_no_stub_is_found_for_request assert_raise Faraday::Adapter::Test::Stubs::NotFound do @conn.get('/invalid'){ [200, {}, []] } end end end end ruby-faraday-0.8.7/test/adapters/typhoeus_test.rb000066400000000000000000000004641214522212000221350ustar00rootroot00000000000000require File.expand_path('../integration', __FILE__) module Adapters class TyphoeusTest < Faraday::TestCase def adapter() :typhoeus end Integration.apply(self, :Parallel) do # https://github.com/dbalatero/typhoeus/issues/75 undef :test_GET_with_body end unless jruby? end end ruby-faraday-0.8.7/test/authentication_middleware_test.rb000066400000000000000000000043661214522212000236730ustar00rootroot00000000000000require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class AuthenticationMiddlewareTest < Faraday::TestCase def conn Faraday::Connection.new('http://example.net/') do |builder| yield builder builder.adapter :test do |stub| stub.get('/auth-echo') do |env| [200, {}, env[:request_headers]['Authorization']] end end end end def test_basic_middleware_adds_basic_header response = conn { |b| b.request :basic_auth, 'aladdin', 'opensesame' }.get('/auth-echo') assert_equal 'Basic YWxhZGRpbjpvcGVuc2VzYW1l', response.body end def test_basic_middleware_adds_basic_header_correctly_with_long_values response = conn { |b| b.request :basic_auth, 'A' * 255, '' }.get('/auth-echo') assert_equal "Basic #{'QUFB' * 85}Og==", response.body end def test_basic_middleware_does_not_interfere_with_existing_authorization response = conn { |b| b.request :basic_auth, 'aladdin', 'opensesame' }. get('/auth-echo', nil, :authorization => 'Token token="bar"') assert_equal 'Token token="bar"', response.body end def test_token_middleware_adds_token_header response = conn { |b| b.request :token_auth, 'quux' }.get('/auth-echo') assert_equal 'Token token="quux"', response.body end def test_token_middleware_includes_other_values_if_provided response = conn { |b| b.request :token_auth, 'baz', :foo => 42 }.get('/auth-echo') assert_match /^Token /, response.body assert_match /token="baz"/, response.body assert_match /foo="42"/, response.body end def test_token_middleware_does_not_interfere_with_existing_authorization response = conn { |b| b.request :token_auth, 'quux' }. get('/auth-echo', nil, :authorization => 'Token token="bar"') assert_equal 'Token token="bar"', response.body end def test_authorization_middleware_with_string response = conn { |b| b.request :authorization, 'custom', 'abc def' }.get('/auth-echo') assert_match /^custom abc def$/, response.body end def test_authorization_middleware_with_hash response = conn { |b| b.request :authorization, 'baz', :foo => 42 }.get('/auth-echo') assert_match /^baz /, response.body assert_match /foo="42"/, response.body end end ruby-faraday-0.8.7/test/composite_read_io_test.rb000066400000000000000000000060121214522212000221310ustar00rootroot00000000000000require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) require 'stringio' class CompositeReadIOTest < Faraday::TestCase Part = Struct.new(:to_io) do def length() to_io.string.length end end def part(str) Part.new StringIO.new(str) end def composite_io(*parts) Faraday::CompositeReadIO.new(*parts) end def test_empty io = composite_io assert_equal 0, io.length assert_equal "", io.read end def test_empty_returns_nil_for_limited_read assert_nil composite_io.read(1) end def test_empty_parts_returns_nil_for_limited_read io = composite_io(part(""), part("")) assert_nil io.read(1) end def test_multipart_read_all io = composite_io(part("abcd"), part("1234")) assert_equal 8, io.length assert_equal "abcd1234", io.read end def test_multipart_read_limited io = composite_io(part("abcd"), part("1234")) assert_equal "abc", io.read(3) assert_equal "d12", io.read(3) assert_equal "34", io.read(3) assert_equal nil, io.read(3) assert_equal nil, io.read(3) end def test_multipart_read_limited_size_larger_than_part io = composite_io(part("abcd"), part("1234")) assert_equal "abcd12", io.read(6) assert_equal "34", io.read(6) assert_equal nil, io.read(6) end def test_multipart_read_with_blank_parts io = composite_io(part(""), part("abcd"), part(""), part("1234"), part("")) assert_equal "abcd12", io.read(6) assert_equal "34", io.read(6) assert_equal nil, io.read(6) end def test_multipart_rewind io = composite_io(part("abcd"), part("1234")) assert_equal "abc", io.read(3) assert_equal "d12", io.read(3) io.rewind assert_equal "abc", io.read(3) assert_equal "d1234", io.read(5) assert_equal nil, io.read(3) io.rewind assert_equal "ab", io.read(2) end # JRuby enforces types to copy_stream to be String or IO if IO.respond_to?(:copy_stream) && !jruby? def test_compatible_with_copy_stream target_io = StringIO.new def target_io.ensure_open_and_writable # Rubinius compatibility end io = composite_io(part("abcd"), part("1234")) Faraday::Timer.timeout(1) do IO.copy_stream(io, target_io) end assert_equal "abcd1234", target_io.string end end unless RUBY_VERSION < '1.9' def test_read_from_multibyte File.open(File.dirname(__FILE__) + '/multibyte.txt') do |utf8| io = composite_io(part("\x86"), Part.new(utf8)) assert_equal bin("\x86\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\n"), io.read end end def test_limited_from_multibyte File.open(File.dirname(__FILE__) + '/multibyte.txt') do |utf8| io = composite_io(part("\x86"), Part.new(utf8)) assert_equal bin("\x86\xE3\x83"), io.read(3) assert_equal bin("\x95\xE3\x82"), io.read(3) assert_equal bin("\xA1\xE3\x82\xA4\xE3\x83\xAB\n"), io.read(8) end end end def bin(str) str.force_encoding("BINARY") if str.respond_to?(:force_encoding) str end end ruby-faraday-0.8.7/test/connection_test.rb000066400000000000000000000276731214522212000206240ustar00rootroot00000000000000require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) require 'uri' class TestConnection < Faraday::TestCase def with_env(key, proxy) old_value = ENV.fetch(key, false) ENV[key] = proxy begin yield ensure if old_value == false ENV.delete key else ENV[key] = old_value end end end def test_initialize_parses_host_out_of_given_url conn = Faraday::Connection.new "http://sushi.com" assert_equal 'sushi.com', conn.host end def test_initialize_inherits_default_port_out_of_given_url conn = Faraday::Connection.new "http://sushi.com" assert_equal 80, conn.port end def test_initialize_parses_scheme_out_of_given_url conn = Faraday::Connection.new "http://sushi.com" assert_equal 'http', conn.scheme end def test_initialize_parses_port_out_of_given_url conn = Faraday::Connection.new "http://sushi.com:815" assert_equal 815, conn.port end def test_initialize_parses_nil_path_prefix_out_of_given_url conn = Faraday::Connection.new "http://sushi.com" assert_equal '/', conn.path_prefix end def test_initialize_parses_path_prefix_out_of_given_url conn = Faraday::Connection.new "http://sushi.com/fish" assert_equal '/fish', conn.path_prefix end def test_initialize_parses_path_prefix_out_of_given_url_option conn = Faraday::Connection.new :url => "http://sushi.com/fish" assert_equal '/fish', conn.path_prefix end def test_initialize_stores_default_params_from_options conn = Faraday::Connection.new :params => {:a => 1} assert_equal({'a' => 1}, conn.params) end def test_initialize_stores_default_params_from_uri conn = Faraday::Connection.new "http://sushi.com/fish?a=1" assert_equal({'a' => '1'}, conn.params) end def test_initialize_stores_default_params_from_uri_and_options conn = Faraday::Connection.new "http://sushi.com/fish?a=1&b=2", :params => {'a' => 3} assert_equal({'a' => 3, 'b' => '2'}, conn.params) end def test_initialize_stores_default_headers_from_options conn = Faraday::Connection.new :headers => {:user_agent => 'Faraday'} assert_equal 'Faraday', conn.headers['User-agent'] end def test_basic_auth_sets_header conn = Faraday::Connection.new assert_nil conn.headers['Authorization'] conn.basic_auth 'Aladdin', 'open sesame' assert auth = conn.headers['Authorization'] assert_equal 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', auth end def test_auto_parses_basic_auth_from_url_and_unescapes conn = Faraday::Connection.new :url => "http://foo%40bar.com:pass%20word@sushi.com/fish" assert auth = conn.headers['Authorization'] assert_equal Faraday::Request::BasicAuthentication.header("foo@bar.com", "pass word"), auth end def test_token_auth_sets_header conn = Faraday::Connection.new assert_nil conn.headers['Authorization'] conn.token_auth 'abcdef', :nonce => 'abc' assert auth = conn.headers['Authorization'] assert_match /^Token /, auth assert_match /token="abcdef"/, auth assert_match /nonce="abc"/, auth end def test_build_url_uses_connection_host_as_default_uri_host conn = Faraday::Connection.new conn.host = 'sushi.com' uri = conn.build_url("/sake.html") assert_equal 'sushi.com', uri.host end def test_build_url_overrides_connection_port_for_absolute_urls conn = Faraday::Connection.new conn.port = 23 uri = conn.build_url("http://sushi.com") assert_equal 80, uri.port end def test_build_url_uses_connection_scheme_as_default_uri_scheme conn = Faraday::Connection.new 'http://sushi.com' uri = conn.build_url("/sake.html") assert_equal 'http', uri.scheme end def test_build_url_uses_connection_path_prefix_to_customize_path conn = Faraday::Connection.new conn.path_prefix = '/fish' uri = conn.build_url("sake.html") assert_equal '/fish/sake.html', uri.path end def test_build_url_uses_root_connection_path_prefix_to_customize_path conn = Faraday::Connection.new conn.path_prefix = '/' uri = conn.build_url("sake.html") assert_equal '/sake.html', uri.path end def test_build_url_forces_connection_path_prefix_to_be_absolute conn = Faraday::Connection.new conn.path_prefix = 'fish' uri = conn.build_url("sake.html") assert_equal '/fish/sake.html', uri.path end def test_build_url_ignores_connection_path_prefix_trailing_slash conn = Faraday::Connection.new conn.path_prefix = '/fish/' uri = conn.build_url("sake.html") assert_equal '/fish/sake.html', uri.path end def test_build_url_allows_absolute_uri_to_ignore_connection_path_prefix conn = Faraday::Connection.new conn.path_prefix = '/fish' uri = conn.build_url("/sake.html") assert_equal '/sake.html', uri.path end def test_build_url_parses_url_params_into_path conn = Faraday::Connection.new uri = conn.build_url("http://sushi.com/sake.html") assert_equal '/sake.html', uri.path end def test_build_url_doesnt_add_ending_slash_given_nil_url conn = Faraday::Connection.new conn.url_prefix = "http://sushi.com/nigiri" uri = conn.build_url(nil) assert_equal "/nigiri", uri.path end def test_build_url_doesnt_add_ending_slash_given_empty_url conn = Faraday::Connection.new conn.url_prefix = "http://sushi.com/nigiri" uri = conn.build_url('') assert_equal "/nigiri", uri.path end def test_build_url_parses_url_params_into_query conn = Faraday::Connection.new uri = conn.build_url("http://sushi.com/sake.html", 'a[b]' => '1 + 2') assert_equal "a%5Bb%5D=1+%2B+2", uri.query end def test_build_url_escapes_per_spec conn = Faraday::Connection.new uri = conn.build_url('http:/', 'a' => '1+2 foo~bar.-baz') assert_equal "a=1%2B2+foo~bar.-baz", uri.query end def test_build_url_bracketizes_nested_params_in_query conn = Faraday::Connection.new uri = conn.build_url("http://sushi.com/sake.html", 'a' => {'b' => 'c'}) assert_equal "a%5Bb%5D=c", uri.query end def test_build_url_parses_url conn = Faraday::Connection.new uri = conn.build_url("http://sushi.com/sake.html") assert_equal "http", uri.scheme assert_equal "sushi.com", uri.host assert_equal '/sake.html', uri.path end def test_build_url_parses_url_and_changes_scheme conn = Faraday::Connection.new :url => "http://sushi.com/sushi" conn.scheme = 'https' uri = conn.build_url("sake.html") assert_equal 'https://sushi.com/sushi/sake.html', uri.to_s end def test_build_url_handles_uri_instances conn = Faraday::Connection.new uri = conn.build_url(URI('/sake.html')) assert_equal '/sake.html', uri.path end def test_proxy_accepts_string with_env 'http_proxy', "http://duncan.proxy.com:80" do conn = Faraday::Connection.new conn.proxy 'http://proxy.com' assert_equal 'proxy.com', conn.proxy[:uri].host assert_equal [:uri], conn.proxy.keys end end def test_proxy_accepts_uri with_env 'http_proxy', "http://duncan.proxy.com:80" do conn = Faraday::Connection.new conn.proxy URI.parse('http://proxy.com') assert_equal 'proxy.com', conn.proxy[:uri].host assert_equal [:uri], conn.proxy.keys end end def test_proxy_accepts_hash_with_string_uri with_env 'http_proxy', "http://duncan.proxy.com:80" do conn = Faraday::Connection.new conn.proxy :uri => 'http://proxy.com', :user => 'rick' assert_equal 'proxy.com', conn.proxy[:uri].host assert_equal 'rick', conn.proxy[:user] end end def test_proxy_accepts_hash with_env 'http_proxy', "http://duncan.proxy.com:80" do conn = Faraday::Connection.new conn.proxy :uri => URI.parse('http://proxy.com'), :user => 'rick' assert_equal 'proxy.com', conn.proxy[:uri].host assert_equal 'rick', conn.proxy[:user] end end def test_proxy_accepts_http_env with_env 'http_proxy', "http://duncan.proxy.com:80" do conn = Faraday::Connection.new assert_equal 'duncan.proxy.com', conn.proxy[:uri].host end end def test_proxy_accepts_http_env_with_auth with_env 'http_proxy', "http://a%40b:my%20pass@duncan.proxy.com:80" do conn = Faraday::Connection.new assert_equal 'a@b', conn.proxy[:user] assert_equal 'my pass', conn.proxy[:password] end end def test_proxy_requires_uri conn = Faraday::Connection.new assert_raises ArgumentError do conn.proxy :uri => :bad_uri, :user => 'rick' end end def test_dups_connection_object conn = Faraday::Connection.new 'http://sushi.com/foo', :ssl => { :verify => :none }, :headers => {'content-type' => 'text/plain'}, :params => {'a'=>'1'} other = conn.dup assert_equal conn.build_url(''), other.build_url('') assert_equal 'text/plain', other.headers['content-type'] assert_equal '1', other.params['a'] other.basic_auth('', '') other.headers['content-length'] = 12 other.params['b'] = '2' assert_equal 2, other.builder.handlers.size assert_equal 2, conn.builder.handlers.size assert !conn.headers.key?('content-length') assert !conn.params.key?('b') end def test_init_with_block conn = Faraday::Connection.new { } assert_equal 0, conn.builder.handlers.size end def test_init_with_block_yields_connection conn = Faraday::Connection.new(:params => {'a'=>'1'}) { |faraday| faraday.adapter :net_http faraday.url_prefix = 'http://sushi.com/omnom' assert_equal '1', faraday.params['a'] } assert_equal 1, conn.builder.handlers.size assert_equal '/omnom', conn.path_prefix end end class TestRequestParams < Faraday::TestCase def create_connection(*args) @conn = Faraday::Connection.new(*args) do |conn| yield conn if block_given? class << conn undef app def app() lambda { |env| env } end end end end def get(*args) env = @conn.get(*args) do |req| yield req if block_given? end env[:url].query end def assert_query_equal(expected, query) assert_equal expected, query.split('&').sort end def test_merges_connection_and_request_params create_connection 'http://a.co/?token=abc', :params => {'format' => 'json'} query = get '?page=1', :limit => 5 assert_query_equal %w[format=json limit=5 page=1 token=abc], query end def test_overrides_connection_params create_connection 'http://a.co/?a=a&b=b&c=c', :params => {:a => 'A'} do |conn| conn.params[:b] = 'B' assert_equal 'c', conn.params[:c] end assert_query_equal %w[a=A b=B c=c], get end def test_all_overrides_connection_params create_connection 'http://a.co/?a=a', :params => {:c => 'c'} do |conn| conn.params = {'b' => 'b'} end assert_query_equal %w[b=b], get end def test_overrides_request_params create_connection query = get '?p=1&a=a', :p => 2 assert_query_equal %w[a=a p=2], query end def test_overrides_request_params_block create_connection query = get '?p=1&a=a', :p => 2 do |req| req.params[:p] = 3 end assert_query_equal %w[a=a p=3], query end def test_overrides_request_params_block_url create_connection query = get nil, :p => 2 do |req| req.url '?p=1&a=a', 'p' => 3 end assert_query_equal %w[a=a p=3], query end def test_overrides_all_request_params create_connection :params => {:c => 'c'} query = get '?p=1&a=a', :p => 2 do |req| assert_equal 'a', req.params[:a] assert_equal 'c', req.params['c'] assert_equal 2, req.params['p'] req.params = {:b => 'b'} assert_equal 'b', req.params['b'] end assert_query_equal %w[b=b], query end def test_array_params_in_url create_connection 'http://a.co/page1?color[]=red&color[]=blue' query = get assert_equal "color%5B%5D=red&color%5B%5D=blue", query end def test_array_params_in_params create_connection 'http://a.co/page1', :params => {:color => ['red', 'blue']} query = get assert_equal "color%5B%5D=red&color%5B%5D=blue", query end end ruby-faraday-0.8.7/test/env_test.rb000066400000000000000000000124041214522212000172370ustar00rootroot00000000000000require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class EnvTest < Faraday::TestCase def setup @conn = Faraday.new :url => 'http://sushi.com/api', :headers => {'Mime-Version' => '1.0'}, :request => {:oauth => {:consumer_key => 'anonymous'}} @conn.options[:timeout] = 3 @conn.options[:open_timeout] = 5 @conn.ssl[:verify] = false @conn.proxy 'http://proxy.com' end def test_request_create_stores_method env = make_env(:get) assert_equal :get, env[:method] end def test_request_create_stores_uri env = make_env do |req| req.url 'foo.json', 'a' => 1 end assert_equal 'http://sushi.com/api/foo.json?a=1', env[:url].to_s end def test_request_create_stores_headers env = make_env do |req| req['Server'] = 'Faraday' end headers = env[:request_headers] assert_equal '1.0', headers['mime-version'] assert_equal 'Faraday', headers['server'] end def test_request_create_stores_body env = make_env do |req| req.body = 'hi' end assert_equal 'hi', env[:body] end def test_global_request_options env = make_env assert_equal 3, env[:request][:timeout] assert_equal 5, env[:request][:open_timeout] end def test_per_request_options env = make_env do |req| req.options[:timeout] = 10 req.options[:custom] = true req.options[:oauth][:consumer_secret] = 'xyz' end assert_equal 10, env[:request][:timeout] assert_equal 5, env[:request][:open_timeout] assert_equal true, env[:request][:custom] oauth_expected = {:consumer_secret => 'xyz', :consumer_key => 'anonymous'} assert_equal oauth_expected, env[:request][:oauth] end def test_request_create_stores_ssl_options env = make_env assert_equal false, env[:ssl][:verify] end def test_request_create_stores_proxy_options env = make_env assert_equal 'proxy.com', env[:request][:proxy][:uri].host end private def make_env(method = :get, connection = @conn, &block) request = connection.build_request(method, &block) request.to_env(connection) end end class HeadersTest < Faraday::TestCase def setup @headers = Faraday::Utils::Headers.new end def test_normalizes_different_capitalizations @headers['Content-Type'] = 'application/json' assert_equal ['Content-Type'], @headers.keys assert_equal 'application/json', @headers['Content-Type'] assert_equal 'application/json', @headers['CONTENT-TYPE'] assert_equal 'application/json', @headers['content-type'] assert @headers.include?('content-type') @headers['content-type'] = 'application/xml' assert_equal ['Content-Type'], @headers.keys assert_equal 'application/xml', @headers['Content-Type'] assert_equal 'application/xml', @headers['CONTENT-TYPE'] assert_equal 'application/xml', @headers['content-type'] end def test_delete_key @headers['Content-Type'] = 'application/json' assert_equal 1, @headers.size assert @headers.include?('content-type') assert_equal 'application/json', @headers.delete('content-type') assert_equal 0, @headers.size assert !@headers.include?('content-type') assert_equal nil, @headers.delete('content-type') end def test_parse_response_headers_leaves_http_status_line_out @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n") assert_equal %w(Content-Type), @headers.keys end def test_parse_response_headers_parses_lower_cased_header_name_and_value @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n") assert_equal 'text/html', @headers['content-type'] end def test_parse_response_headers_parses_lower_cased_header_name_and_value_with_colon @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://sushi.com/\r\n\r\n") assert_equal 'http://sushi.com/', @headers['location'] end def test_parse_response_headers_parses_blank_lines @headers.parse("HTTP/1.1 200 OK\r\n\r\nContent-Type: text/html\r\n\r\n") assert_equal 'text/html', @headers['content-type'] end end class ResponseTest < Faraday::TestCase def setup @env = { :status => 404, :body => 'yikes', :response_headers => Faraday::Utils::Headers.new('Content-Type' => 'text/plain') } @response = Faraday::Response.new @env end def test_finished assert @response.finished? end def test_error_on_finish assert_raises RuntimeError do @response.finish({}) end end def test_not_success assert !@response.success? end def test_status assert_equal 404, @response.status end def test_body assert_equal 'yikes', @response.body end def test_headers assert_equal 'text/plain', @response.headers['Content-Type'] assert_equal 'text/plain', @response['content-type'] end def test_apply_request @response.apply_request :body => 'a=b', :method => :post assert_equal 'yikes', @response.body assert_equal :post, @response.env[:method] end def test_marshal @response = Faraday::Response.new @response.on_complete { } @response.finish @env.merge(:custom => 'moo') loaded = Marshal.load Marshal.dump(@response) assert_nil loaded.env[:custom] assert_equal %w[body response_headers status], loaded.env.keys.map { |k| k.to_s }.sort end end ruby-faraday-0.8.7/test/helper.rb000066400000000000000000000026561214522212000166770ustar00rootroot00000000000000unless ENV['CI'] begin require 'simplecov' SimpleCov.start do add_filter 'test' end rescue LoadError end end require 'test/unit' if ENV['LEFTRIGHT'] begin require 'leftright' rescue LoadError puts "Run `gem install leftright` to install leftright." end end require File.expand_path('../../lib/faraday', __FILE__) begin require 'ruby-debug' rescue LoadError # ignore else Debugger.start end require 'stringio' require 'uri' module Faraday module LiveServerConfig def live_server=(value) @@live_server = case value when /^http/ URI(value) when /./ URI('http://127.0.0.1:4567') end end def live_server? defined? @@live_server end # Returns an object that responds to `host` and `port`. def live_server live_server? and @@live_server end end class TestCase < Test::Unit::TestCase extend LiveServerConfig self.live_server = ENV['LIVE'] def test_default assert true end unless defined? ::MiniTest def capture_warnings old, $stderr = $stderr, StringIO.new begin yield $stderr.string ensure $stderr = old end end def self.jruby? defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE end def self.rbx? defined? RUBY_ENGINE and 'rbx' == RUBY_ENGINE end def self.ssl_mode? ENV['SSL'] == 'yes' end end end ruby-faraday-0.8.7/test/live_server.rb000066400000000000000000000021701214522212000177340ustar00rootroot00000000000000require 'sinatra/base' module Faraday class LiveServer < Sinatra::Base set :environment, :test disable :logging disable :protection [:get, :post, :put, :patch, :delete, :options].each do |method| send(method, '/echo') do kind = request.request_method.downcase out = kind.dup out << ' ?' << request.GET.inspect if request.GET.any? out << ' ' << request.POST.inspect if request.POST.any? content_type 'text/plain' return out end end get '/echo_header' do header = "HTTP_#{params[:name].tr('-', '_').upcase}" request.env.fetch(header) { 'NONE' } end post '/file' do if params[:uploaded_file].respond_to? :each_key "file %s %s" % [ params[:uploaded_file][:filename], params[:uploaded_file][:type]] else status 400 end end get '/multi' do [200, { 'Set-Cookie' => 'one, two' }, ''] end get '/slow' do sleep 10 [200, {}, 'ok'] end get '/ssl' do request.secure?.to_s end error do |e| "#{e.class}\n#{e.to_s}\n#{e.backtrace.join("\n")}" end end end if $0 == __FILE__ Faraday::LiveServer.run! end ruby-faraday-0.8.7/test/middleware/000077500000000000000000000000001214522212000171775ustar00rootroot00000000000000ruby-faraday-0.8.7/test/middleware/retry_test.rb000066400000000000000000000010351214522212000217270ustar00rootroot00000000000000require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper")) module Middleware class RetryTest < Faraday::TestCase def setup @stubs = Faraday::Adapter::Test::Stubs.new @conn = Faraday.new do |b| b.request :retry, 2 b.adapter :test, @stubs end end def test_retries times_called = 0 @stubs.post("/echo") do times_called += 1 raise "Error occurred" end @conn.post("/echo") rescue nil assert_equal times_called, 3 end end end ruby-faraday-0.8.7/test/middleware_stack_test.rb000066400000000000000000000106461214522212000217570ustar00rootroot00000000000000require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class MiddlewareStackTest < Faraday::TestCase # mock handler classes class Handler < Struct.new(:app) def call(env) (env[:request_headers]['X-Middleware'] ||= '') << ":#{self.class.name.split('::').last}" app.call(env) end end class Apple < Handler; end class Orange < Handler; end class Banana < Handler; end class Broken < Faraday::Middleware dependency 'zomg/i_dont/exist' end def setup @conn = Faraday::Connection.new @builder = @conn.builder end def test_sets_default_adapter_if_none_set default_middleware = Faraday::Request.lookup_middleware :url_encoded default_adapter_klass = Faraday::Adapter.lookup_middleware Faraday.default_adapter assert @builder[0] == default_middleware assert @builder[1] == default_adapter_klass end def test_allows_rebuilding build_stack Apple build_stack Orange assert_handlers %w[Orange] end def test_allows_extending build_stack Apple @conn.use Orange assert_handlers %w[Apple Orange] end def test_builder_is_passed_to_new_faraday_connection new_conn = Faraday::Connection.new :builder => @builder assert_equal @builder, new_conn.builder end def test_insert_before build_stack Apple, Orange @builder.insert_before Apple, Banana assert_handlers %w[Banana Apple Orange] end def test_insert_after build_stack Apple, Orange @builder.insert_after Apple, Banana assert_handlers %w[Apple Banana Orange] end def test_swap_handlers build_stack Apple, Orange @builder.swap Apple, Banana assert_handlers %w[Banana Orange] end def test_delete_handler build_stack Apple, Orange @builder.delete Apple assert_handlers %w[Orange] end def test_stack_is_locked_after_making_requests build_stack Apple assert !@builder.locked? @conn.get('/') assert @builder.locked? assert_raises Faraday::Builder::StackLocked do @conn.use Orange end end def test_duped_stack_is_unlocked build_stack Apple assert !@builder.locked? @builder.lock! assert @builder.locked? duped_connection = @conn.dup assert_equal @builder, duped_connection.builder assert !duped_connection.builder.locked? end def test_handler_comparison build_stack Apple assert_equal @builder.handlers.first, Apple assert_equal @builder.handlers[0,1], [Apple] assert_equal @builder.handlers.first, Faraday::Builder::Handler.new(Apple) end def test_unregistered_symbol err = assert_raise(RuntimeError) { build_stack :apple } assert_equal ":apple is not registered on Faraday::Middleware", err.message end def test_registered_symbol Faraday.register_middleware :apple => Apple begin build_stack :apple assert_handlers %w[Apple] ensure unregister_middleware Faraday::Middleware, :apple end end def test_registered_symbol_with_proc Faraday.register_middleware :apple => lambda { Apple } begin build_stack :apple assert_handlers %w[Apple] ensure unregister_middleware Faraday::Middleware, :apple end end def test_registered_symbol_with_type Faraday.register_middleware :request, :orange => Orange begin build_stack {|b| b.request :orange } assert_handlers %w[Orange] ensure unregister_middleware Faraday::Request, :orange end end def test_missing_dependencies build_stack Broken err = assert_raises RuntimeError do @conn.get('/') end assert_match "missing dependency for MiddlewareStackTest::Broken: ", err.message assert_match "zomg/i_dont/exist", err.message end private # make a stack with test adapter that reflects the order of middleware def build_stack(*handlers) @builder.build do |b| handlers.each { |handler| b.use(*handler) } yield b if block_given? b.adapter :test do |stub| stub.get '/' do |env| # echo the "X-Middleware" request header in the body [200, {}, env[:request_headers]['X-Middleware'].to_s] end end end end def assert_handlers(list) echoed_list = @conn.get('/').body.to_s.split(':') echoed_list.shift if echoed_list.first == '' assert_equal list, echoed_list end def unregister_middleware(component, key) # TODO: unregister API? component.instance_variable_get('@registered_middleware').delete(key) end end ruby-faraday-0.8.7/test/request_middleware_test.rb000066400000000000000000000102141214522212000223310ustar00rootroot00000000000000# encoding: utf-8 require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) Faraday::CompositeReadIO.class_eval { attr_reader :ios } class RequestMiddlewareTest < Faraday::TestCase def setup @conn = Faraday.new do |b| b.request :multipart b.request :url_encoded b.adapter :test do |stub| stub.post('/echo') do |env| posted_as = env[:request_headers]['Content-Type'] [200, {'Content-Type' => posted_as}, env[:body]] end end end end def with_utf8 if defined?(RUBY_VERSION) && RUBY_VERSION.match(/1.8.\d/) begin previous_kcode = $KCODE $KCODE = "UTF8" yield ensure $KCODE = previous_kcode end else yield end end def test_does_nothing_without_payload response = @conn.post('/echo') assert_nil response.headers['Content-Type'] assert response.body.empty? end def test_ignores_custom_content_type response = @conn.post('/echo', { :some => 'data' }, 'content-type' => 'application/x-foo') assert_equal 'application/x-foo', response.headers['Content-Type'] assert_equal({ :some => 'data' }, response.body) end def test_url_encoded_no_header response = @conn.post('/echo', { :fruit => %w[apples oranges] }) assert_equal 'application/x-www-form-urlencoded', response.headers['Content-Type'] assert_equal 'fruit%5B%5D=apples&fruit%5B%5D=oranges', response.body end def test_url_encoded_with_header response = @conn.post('/echo', {'a'=>123}, 'content-type' => 'application/x-www-form-urlencoded') assert_equal 'application/x-www-form-urlencoded', response.headers['Content-Type'] assert_equal 'a=123', response.body end def test_url_encoded_nested response = @conn.post('/echo', { :user => {:name => 'Mislav', :web => 'mislav.net'} }) assert_equal 'application/x-www-form-urlencoded', response.headers['Content-Type'] expected = { 'user' => {'name' => 'Mislav', 'web' => 'mislav.net'} } assert_equal expected, Faraday::Utils.parse_nested_query(response.body) end def test_url_encoded_unicode err = capture_warnings { response = @conn.post('/echo', {:str => "eé cç aã aâ"}) assert_equal "str=e%C3%A9+c%C3%A7+a%C3%A3+a%C3%A2", response.body } assert err.empty? end def test_url_encoded_unicode_with_kcode_set with_utf8 do err = capture_warnings { response = @conn.post('/echo', {:str => "eé cç aã aâ"}) assert_equal "str=e%C3%A9+c%C3%A7+a%C3%A3+a%C3%A2", response.body } assert err.empty? end end def test_url_encoded_nested_keys response = @conn.post('/echo', {'a'=>{'b'=>{'c'=>['d']}}}) assert_equal "a%5Bb%5D%5Bc%5D%5B%5D=d", response.body end def test_multipart # assume params are out of order regexes = [ /name\=\"a\"/, /name=\"b\[c\]\"\; filename\=\"request_middleware_test\.rb\"/, /name=\"b\[d\]\"/] payload = {:a => 1, :b => {:c => Faraday::UploadIO.new(__FILE__, 'text/x-ruby'), :d => 2}} response = @conn.post('/echo', payload) assert_kind_of Faraday::CompositeReadIO, response.body assert_equal "multipart/form-data;boundary=%s" % Faraday::Request::Multipart::DEFAULT_BOUNDARY, response.headers['Content-Type'] response.body.send(:ios).map{|io| io.read}.each do |io| if re = regexes.detect { |r| io =~ r } regexes.delete re end end assert_equal [], regexes end def test_multipart_with_arrays # assume params are out of order regexes = [ /name\=\"a\"/, /name=\"b\[\]\[c\]\"\; filename\=\"request_middleware_test\.rb\"/, /name=\"b\[\]\[d\]\"/] payload = {:a => 1, :b =>[{:c => Faraday::UploadIO.new(__FILE__, 'text/x-ruby'), :d => 2}]} response = @conn.post('/echo', payload) assert_kind_of Faraday::CompositeReadIO, response.body assert_equal "multipart/form-data;boundary=%s" % Faraday::Request::Multipart::DEFAULT_BOUNDARY, response.headers['Content-Type'] response.body.send(:ios).map{|io| io.read}.each do |io| if re = regexes.detect { |r| io =~ r } regexes.delete re end end assert_equal [], regexes end end ruby-faraday-0.8.7/test/response_middleware_test.rb000066400000000000000000000036361214522212000225110ustar00rootroot00000000000000require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) class ResponseMiddlewareTest < Faraday::TestCase def setup @conn = Faraday.new do |b| b.response :raise_error b.adapter :test do |stub| stub.get('ok') { [200, {'Content-Type' => 'text/html'}, ''] } stub.get('not-found') { [404, {'X-Reason' => 'because'}, 'keep looking'] } stub.get('error') { [500, {'X-Error' => 'bailout'}, 'fail'] } end end end class ResponseUpcaser < Faraday::Response::Middleware def parse(body) body.upcase end end def test_success assert_nothing_raised do @conn.get('ok') end end def test_raises_not_found error = assert_raises Faraday::Error::ResourceNotFound do @conn.get('not-found') end assert_equal 'the server responded with status 404', error.message assert_equal 'because', error.response[:headers]['X-Reason'] end def test_raises_error error = assert_raises Faraday::Error::ClientError do @conn.get('error') end assert_equal 'the server responded with status 500', error.message assert_equal 'bailout', error.response[:headers]['X-Error'] end def test_upcase @conn.builder.insert(0, ResponseUpcaser) assert_equal '', @conn.get('ok').body end end class ResponseNoBodyMiddleWareTest < Faraday::TestCase def setup @conn = Faraday.new do |b| b.response :raise_error b.adapter :test do |stub| stub.get('not_modified') { [304, nil, nil] } stub.get('no_content') { [204, nil, nil] } end end @conn.builder.insert(0, NotCalled) end class NotCalled < Faraday::Response::Middleware def parse(body) raise "this should not be called" end end def test_204 assert_equal nil, @conn.get('no_content').body end def test_304 assert_equal nil, @conn.get('not_modified').body end end