faraday 0.9.0.rc5 → 0.9.0.rc6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile +6 -5
  2. data/README.md +0 -22
  3. data/faraday.gemspec +2 -2
  4. data/lib/faraday.rb +6 -2
  5. data/lib/faraday/adapter/em_http.rb +34 -14
  6. data/lib/faraday/adapter/em_http_ssl_patch.rb +56 -0
  7. data/lib/faraday/adapter/em_synchrony.rb +21 -20
  8. data/lib/faraday/adapter/excon.rb +16 -0
  9. data/lib/faraday/adapter/httpclient.rb +14 -0
  10. data/lib/faraday/adapter/net_http.rb +6 -2
  11. data/lib/faraday/adapter/net_http_persistent.rb +15 -3
  12. data/lib/faraday/adapter/patron.rb +13 -11
  13. data/lib/faraday/adapter/typhoeus.rb +11 -0
  14. data/lib/faraday/autoload.rb +2 -4
  15. data/lib/faraday/connection.rb +30 -3
  16. data/lib/faraday/error.rb +4 -1
  17. data/lib/faraday/options.rb +117 -23
  18. data/lib/faraday/request/instrumentation.rb +1 -3
  19. data/lib/faraday/request/multipart.rb +1 -1
  20. data/lib/faraday/request/retry.rb +38 -9
  21. data/lib/faraday/response.rb +1 -2
  22. data/lib/faraday/response/raise_error.rb +3 -0
  23. data/lib/faraday/utils.rb +10 -4
  24. data/script/proxy-server +42 -0
  25. data/script/server +1 -3
  26. data/script/test +35 -7
  27. data/test/adapters/excon_test.rb +4 -0
  28. data/test/adapters/httpclient_test.rb +5 -0
  29. data/test/adapters/integration.rb +48 -2
  30. data/test/adapters/net_http_persistent_test.rb +10 -1
  31. data/test/adapters/patron_test.rb +3 -0
  32. data/test/adapters/rack_test.rb +5 -0
  33. data/test/adapters/typhoeus_test.rb +3 -13
  34. data/test/authentication_middleware_test.rb +6 -6
  35. data/test/connection_test.rb +123 -49
  36. data/test/env_test.rb +19 -1
  37. data/test/helper.rb +2 -4
  38. data/test/live_server.rb +4 -2
  39. data/test/middleware/instrumentation_test.rb +13 -0
  40. data/test/middleware/retry_test.rb +47 -0
  41. data/test/multibyte.txt +1 -0
  42. data/test/options_test.rb +93 -17
  43. data/test/request_middleware_test.rb +6 -6
  44. data/test/utils_test.rb +34 -13
  45. metadata +69 -44
data/Gemfile CHANGED
@@ -1,17 +1,18 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'ffi-ncurses', '~> 0.3', :platforms => :jruby
4
- gem 'jruby-openssl', '~> 0.7', :platforms => :jruby
4
+ gem 'jruby-openssl', '~> 0.8.8', :platforms => :jruby
5
5
  gem 'rake'
6
6
 
7
7
  group :test do
8
8
  gem 'coveralls', :require => false
9
- gem 'em-http-request', '>= 1.0', :require => 'em-http'
10
- gem 'em-synchrony', '>= 1.0', :require => ['em-synchrony', 'em-synchrony/em-http']
11
- gem 'excon', '>= 0.16.1'
9
+ gem 'em-http-request', '>= 1.1', :require => 'em-http'
10
+ gem 'em-synchrony', '>= 1.0.3', :require => ['em-synchrony', 'em-synchrony/em-http']
11
+ gem 'excon', ['>= 0.25.3', '< 0.27.3']
12
12
  gem 'httpclient', '>= 2.2'
13
13
  gem 'leftright', '>= 0.9', :require => false
14
- gem 'minitest', '>= 4.3'
14
+ gem 'mime-types', '~> 1.25', :platforms => :ruby_18
15
+ gem 'minitest', '~> 5.0.5'
15
16
  gem 'net-http-persistent', '>= 2.5', :require => false
16
17
  gem 'patron', '>= 0.4.2', :platforms => :ruby
17
18
  gem 'rack-test', '>= 0.6', :require => 'rack/test'
data/README.md CHANGED
@@ -24,17 +24,6 @@ conn = Faraday.new(:url => 'http://sushi.com') do |faraday|
24
24
  faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
25
25
  end
26
26
 
27
- # set proxy with string
28
- conn.options.proxy = "http://user:password@example.org/"
29
-
30
- # set proxy with hash
31
- conn.options.proxy = { :uri => 'http://user:passwordexample.org' }
32
-
33
- # specify proxy user/pass
34
- conn.options.proxy = { :uri => 'http://user:pass.org',
35
- :user => 'user',
36
- :password => 'pass' }
37
-
38
27
  ## GET ##
39
28
 
40
29
  response = conn.get '/nigiri/sake.json' # GET http://sushi.com/nigiri/sake.json
@@ -64,17 +53,6 @@ conn.get do |req|
64
53
  req.url '/search'
65
54
  req.options.timeout = 5 # open/read timeout in seconds
66
55
  req.options.open_timeout = 2 # connection open timeout in seconds
67
-
68
- # set proxy with string
69
- req.options.proxy = "http://user:password@example.org/"
70
-
71
- # set proxy with hash
72
- req.options.proxy = { :uri => 'http://user:passwordexample.org' }
73
-
74
- # specify proxy user/pass
75
- req.options.proxy = { :uri => 'http://user:pass.org',
76
- :user => 'user',
77
- :password => 'pass' }
78
56
  end
79
57
  ```
80
58
 
@@ -17,13 +17,13 @@ Gem::Specification.new do |spec|
17
17
  spec.homepage = 'https://github.com/lostisland/faraday'
18
18
  spec.licenses = ['MIT']
19
19
 
20
- spec.add_dependency 'multipart-post', '~> 1.1'
20
+ spec.add_dependency 'multipart-post', '~> 1.2.0'
21
21
  spec.add_development_dependency 'bundler', '~> 1.0'
22
22
 
23
23
  spec.files = %w(.document CHANGELOG.md CONTRIBUTING.md Gemfile LICENSE.md README.md Rakefile)
24
24
  spec.files << "#{lib}.gemspec"
25
25
  spec.files += Dir.glob("lib/**/*.rb")
26
- spec.files += Dir.glob("test/**/*.rb")
26
+ spec.files += Dir.glob("test/**/*.{rb,txt}")
27
27
  spec.files += Dir.glob("script/*")
28
28
 
29
29
  dev_null = File.exist?('/dev/null') ? '/dev/null' : 'NUL'
@@ -14,7 +14,7 @@ require 'forwardable'
14
14
  # conn.get '/'
15
15
  #
16
16
  module Faraday
17
- VERSION = "0.9.0.rc5"
17
+ VERSION = "0.9.0.rc6"
18
18
 
19
19
  class << self
20
20
  # Public: Gets or sets the root path that Faraday is being loaded from.
@@ -26,7 +26,7 @@ module Faraday
26
26
 
27
27
  # Public: Gets or sets the Symbol key identifying a default Adapter to use
28
28
  # for the default Faraday::Connection.
29
- attr_accessor :default_adapter
29
+ attr_reader :default_adapter
30
30
 
31
31
  # Public: Sets the default Faraday::Connection for simple scripts that
32
32
  # access the Faraday constant directly.
@@ -238,6 +238,10 @@ module Faraday
238
238
 
239
239
  require_libs "utils", "options", "connection", "rack_builder", "parameters",
240
240
  "middleware", "adapter", "request", "response", "upload_io", "error"
241
+
242
+ if !ENV["FARADAY_NO_AUTOLOAD"]
243
+ require_lib 'autoload'
244
+ end
241
245
  end
242
246
 
243
247
  # not pulling in active-support JUST for this method. And I love this method.
@@ -7,10 +7,10 @@ module Faraday
7
7
  module Options
8
8
  def connection_config(env)
9
9
  options = {}
10
- configure_ssl(options, env)
11
10
  configure_proxy(options, env)
12
11
  configure_timeout(options, env)
13
12
  configure_socket(options, env)
13
+ configure_ssl(options, env)
14
14
  options
15
15
  end
16
16
 
@@ -22,9 +22,6 @@ module Faraday
22
22
  # :file => 'path/to/file', # stream data off disk
23
23
  }
24
24
  configure_compression(options, env)
25
- # configure_proxy_auth
26
- # :proxy => {:authorization => [user, pass]}
27
- # proxy[:username] && proxy[:password]
28
25
  options
29
26
  end
30
27
 
@@ -33,20 +30,12 @@ module Faraday
33
30
  body.respond_to?(:read) ? body.read : body
34
31
  end
35
32
 
36
- def configure_ssl(options, env)
37
- if ssl = env[:ssl]
38
- # :ssl => {
39
- # :private_key_file => '/tmp/server.key',
40
- # :cert_chain_file => '/tmp/server.crt',
41
- # :verify_peer => false
42
- end
43
- end
44
-
45
33
  def configure_proxy(options, env)
46
34
  if proxy = request_options(env)[:proxy]
47
35
  options[:proxy] = {
48
36
  :host => proxy[:uri].host,
49
- :port => proxy[:uri].port
37
+ :port => proxy[:uri].port,
38
+ :authorization => [proxy[:user], proxy[:password]]
50
39
  }
51
40
  end
52
41
  end
@@ -60,6 +49,15 @@ module Faraday
60
49
  end
61
50
  end
62
51
 
52
+ def configure_ssl(options, env)
53
+ if env[:url].scheme == 'https' && env[:ssl]
54
+ options[:ssl] = {
55
+ :cert_chain_file => env[:ssl][:ca_file],
56
+ :verify_peer => env[:ssl].fetch(:verify, true)
57
+ }
58
+ end
59
+ end
60
+
63
61
  def configure_timeout(options, env)
64
62
  timeout, open_timeout = request_options(env).values_at(:timeout, :open_timeout)
65
63
  options[:connect_timeout] = options[:inactivity_timeout] = timeout
@@ -124,6 +122,18 @@ module Faraday
124
122
  }
125
123
  end
126
124
  end
125
+ rescue EventMachine::Connectify::CONNECTError => err
126
+ if err.message.include?("Proxy Authentication Required")
127
+ raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
128
+ else
129
+ raise Error::ConnectionFailed, err
130
+ end
131
+ rescue => err
132
+ if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
133
+ raise Faraday::SSLError, err
134
+ else
135
+ raise
136
+ end
127
137
  end
128
138
 
129
139
  # TODO: reuse the connection to support pipelining
@@ -150,6 +160,8 @@ module Faraday
150
160
  elsif msg == Errno::ECONNREFUSED
151
161
  errklass = Faraday::Error::ConnectionFailed
152
162
  msg = "connection refused"
163
+ elsif msg == "connection closed by server"
164
+ errklass = Faraday::Error::ConnectionFailed
153
165
  end
154
166
  raise errklass, msg
155
167
  end
@@ -215,3 +227,11 @@ module Faraday
215
227
  end
216
228
  end
217
229
  end
230
+
231
+ begin
232
+ require 'openssl'
233
+ rescue LoadError
234
+ warn "Warning: no such file to load -- openssl. Make sure it is installed if you want HTTPS support"
235
+ else
236
+ require 'faraday/adapter/em_http_ssl_patch'
237
+ end if Faraday::Adapter::EMHttp.loaded?
@@ -0,0 +1,56 @@
1
+ require 'openssl'
2
+ require 'em-http'
3
+
4
+ module EmHttpSslPatch
5
+ def ssl_verify_peer(cert_string)
6
+ cert = nil
7
+ begin
8
+ cert = OpenSSL::X509::Certificate.new(cert_string)
9
+ rescue OpenSSL::X509::CertificateError
10
+ return false
11
+ end
12
+
13
+ @last_seen_cert = cert
14
+
15
+ if certificate_store.verify(@last_seen_cert)
16
+ begin
17
+ certificate_store.add_cert(@last_seen_cert)
18
+ rescue OpenSSL::X509::StoreError => e
19
+ raise e unless e.message == 'cert already in hash table'
20
+ end
21
+ true
22
+ else
23
+ raise OpenSSL::SSL::SSLError.new(%(unable to verify the server certificate for "#{host}"))
24
+ end
25
+ end
26
+
27
+ def ssl_handshake_completed
28
+ return true unless verify_peer?
29
+
30
+ unless OpenSSL::SSL.verify_certificate_identity(@last_seen_cert, host)
31
+ raise OpenSSL::SSL::SSLError.new(%(host "#{host}" does not match the server certificate))
32
+ else
33
+ true
34
+ end
35
+ end
36
+
37
+ def verify_peer?
38
+ parent.connopts.tls[:verify_peer]
39
+ end
40
+
41
+ def host
42
+ parent.connopts.host
43
+ end
44
+
45
+ def certificate_store
46
+ @certificate_store ||= begin
47
+ store = OpenSSL::X509::Store.new
48
+ store.set_default_paths
49
+ ca_file = parent.connopts.tls[:cert_chain_file]
50
+ store.add_file(ca_file) if ca_file
51
+ store
52
+ end
53
+ end
54
+ end
55
+
56
+ EventMachine::HttpStubConnection.send(:include, EmHttpSslPatch)
@@ -52,6 +52,8 @@ module Faraday
52
52
  client = block.call
53
53
  end
54
54
 
55
+ raise client.error if client.error
56
+
55
57
  save_response(env, client.response_header.status, client.response) do |resp_headers|
56
58
  client.response_header.each do |name, value|
57
59
  resp_headers[name.to_sym] = value
@@ -62,6 +64,18 @@ module Faraday
62
64
  @app.call env
63
65
  rescue Errno::ECONNREFUSED
64
66
  raise Error::ConnectionFailed, $!
67
+ rescue EventMachine::Connectify::CONNECTError => err
68
+ if err.message.include?("Proxy Authentication Required")
69
+ raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
70
+ else
71
+ raise Error::ConnectionFailed, err
72
+ end
73
+ rescue => err
74
+ if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
75
+ raise Faraday::SSLError, err
76
+ else
77
+ raise
78
+ end
65
79
  end
66
80
  end
67
81
  end
@@ -69,23 +83,10 @@ end
69
83
 
70
84
  require 'faraday/adapter/em_synchrony/parallel_manager'
71
85
 
72
- # add missing patch(), options() methods
73
- EventMachine::HTTPMethods.module_eval do
74
- [:patch, :options].each do |type|
75
- next if method_defined? :"a#{type}"
76
- alias_method :"a#{type}", type if method_defined? type
77
- module_eval %[
78
- def #{type}(options = {}, &blk)
79
- f = Fiber.current
80
- conn = setup_request(:#{type}, options, &blk)
81
- if conn.error.nil?
82
- conn.callback { f.resume(conn) }
83
- conn.errback { f.resume(conn) }
84
- Fiber.yield
85
- else
86
- conn
87
- end
88
- end
89
- ]
90
- end
91
- end
86
+ begin
87
+ require 'openssl'
88
+ rescue LoadError
89
+ warn "Warning: no such file to load -- openssl. Make sure it is installed if you want HTTPS support"
90
+ else
91
+ require 'faraday/adapter/em_http_ssl_patch'
92
+ end if Faraday::Adapter::EMSynchrony.loaded?
@@ -16,6 +16,10 @@ module Faraday
16
16
  opts[:ssl_verify_peer] = !!ssl.fetch(:verify, true)
17
17
  opts[:ssl_ca_path] = ssl[:ca_path] if ssl[:ca_path]
18
18
  opts[:ssl_ca_file] = ssl[:ca_file] if ssl[:ca_file]
19
+ opts[:client_cert] = ssl[:client_cert] if ssl[:client_cert]
20
+ opts[:client_key] = ssl[:client_key] if ssl[:client_key]
21
+ opts[:certificate] = ssl[:certificate] if ssl[:certificate]
22
+ opts[:private_key] = ssl[:private_key] if ssl[:private_key]
19
23
 
20
24
  # https://github.com/geemus/excon/issues/106
21
25
  # https://github.com/jruby/jruby-ossl/issues/19
@@ -33,6 +37,16 @@ module Faraday
33
37
  opts[:connect_timeout] = req[:open_timeout]
34
38
  opts[:write_timeout] = req[:open_timeout]
35
39
  end
40
+
41
+ if req[:proxy]
42
+ opts[:proxy] = {
43
+ :host => req[:proxy][:uri].host,
44
+ :port => req[:proxy][:uri].port,
45
+ :scheme => req[:proxy][:uri].scheme,
46
+ :user => req[:proxy][:user],
47
+ :password => req[:proxy][:password]
48
+ }
49
+ end
36
50
  end
37
51
 
38
52
  conn = ::Excon.new(env[:url].to_s, opts.merge(@connection_options))
@@ -48,6 +62,8 @@ module Faraday
48
62
  rescue ::Excon::Errors::SocketError => err
49
63
  if err.message =~ /\btimeout\b/
50
64
  raise Error::TimeoutError, err
65
+ elsif err.message =~ /\bcertificate\b/
66
+ raise Faraday::SSLError, err
51
67
  else
52
68
  raise Error::ConnectionFailed, err
53
69
  end
@@ -39,6 +39,20 @@ module Faraday
39
39
  @app.call env
40
40
  rescue ::HTTPClient::TimeoutError
41
41
  raise Faraday::Error::TimeoutError, $!
42
+ rescue ::HTTPClient::BadResponseError => err
43
+ if err.message.include?('status 407')
44
+ raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
45
+ else
46
+ raise Faraday::Error::ClientError, $!
47
+ end
48
+ rescue Errno::ECONNREFUSED, EOFError
49
+ raise Faraday::Error::ConnectionFailed, $!
50
+ rescue => err
51
+ if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
52
+ raise Faraday::SSLError, err
53
+ else
54
+ raise
55
+ end
42
56
  end
43
57
 
44
58
  def configure_socket(bind)
@@ -36,8 +36,12 @@ module Faraday
36
36
 
37
37
  begin
38
38
  http_response = perform_request(http, env)
39
- rescue *NET_HTTP_EXCEPTIONS
40
- raise Error::ConnectionFailed, $!
39
+ rescue *NET_HTTP_EXCEPTIONS => err
40
+ if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
41
+ raise Faraday::SSLError, err
42
+ else
43
+ raise Error::ConnectionFailed, err
44
+ end
41
45
  end
42
46
 
43
47
  save_response(env, http_response.code.to_i, http_response.body || '') do |response_headers|
@@ -1,4 +1,6 @@
1
- require 'faraday/adapter/net_http'
1
+ # Rely on autoloading instead of explicit require; helps avoid the "already
2
+ # initialized constant" warning on Ruby 1.8.7 when NetHttp is refereced below.
3
+ # require 'faraday/adapter/net_http'
2
4
 
3
5
  module Faraday
4
6
  class Adapter
@@ -7,8 +9,16 @@ module Faraday
7
9
  dependency 'net/http/persistent'
8
10
 
9
11
  def net_http_connection(env)
10
- Net::HTTP::Persistent.new 'Faraday',
11
- env[:request][:proxy] ? env[:request][:proxy][:uri] : nil
12
+ if proxy = env[:request][:proxy]
13
+ proxy_uri = ::URI::HTTP === proxy[:uri] ? proxy[:uri].dup : ::URI.parse(proxy[:uri].to_s)
14
+ proxy_uri.user = proxy_uri.password = nil
15
+ # awful patch for net-http-persistent 2.8 not unescaping user/password
16
+ (class << proxy_uri; self; end).class_eval do
17
+ define_method(:user) { proxy[:user] }
18
+ define_method(:password) { proxy[:password] }
19
+ end if proxy[:user]
20
+ end
21
+ Net::HTTP::Persistent.new 'Faraday', proxy_uri
12
22
  end
13
23
 
14
24
  def perform_request(http, env)
@@ -16,6 +26,8 @@ module Faraday
16
26
  rescue Net::HTTP::Persistent::Error => error
17
27
  if error.message.include? 'Timeout'
18
28
  raise Faraday::Error::TimeoutError, error
29
+ elsif error.message.include? 'connection refused'
30
+ raise Faraday::Error::ConnectionFailed, error
19
31
  else
20
32
  raise
21
33
  end
@@ -5,7 +5,7 @@ module Faraday
5
5
 
6
6
  def initialize(app, &block)
7
7
  super(app)
8
- @block = block if block_given?
8
+ @block = block
9
9
  end
10
10
 
11
11
  def call(env)
@@ -19,19 +19,19 @@ module Faraday
19
19
  if req = env[:request]
20
20
  session.timeout = session.connect_timeout = req[:timeout] if req[:timeout]
21
21
  session.connect_timeout = req[:open_timeout] if req[:open_timeout]
22
-
22
+
23
23
  if proxy = req[:proxy]
24
- session.proxy = proxy[:uri].to_s
25
- if proxy[:user] && proxy[:password]
26
- prepend_proxy_auth_string(proxy, session)
27
- end
24
+ proxy_uri = proxy[:uri].dup
25
+ proxy_uri.user = proxy[:user] && Utils.escape(proxy[:user]).gsub('+', '%20')
26
+ proxy_uri.password = proxy[:password] && Utils.escape(proxy[:password]).gsub('+', '%20')
27
+ session.proxy = proxy_uri.to_s
28
28
  end
29
29
  end
30
30
 
31
31
  response = begin
32
32
  data = env[:body] ? env[:body].to_s : nil
33
33
  session.request(env[:method], env[:url].to_s, env[:request_headers], :data => data)
34
- rescue Errno::ECONNREFUSED
34
+ rescue Errno::ECONNREFUSED, ::Patron::ConnectionFailed
35
35
  raise Error::ConnectionFailed, $!
36
36
  end
37
37
 
@@ -40,6 +40,12 @@ module Faraday
40
40
  @app.call env
41
41
  rescue ::Patron::TimeoutError => err
42
42
  raise Faraday::Error::TimeoutError, err
43
+ rescue ::Patron::Error => err
44
+ if err.message.include?("code 407")
45
+ raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
46
+ else
47
+ raise Error::ConnectionFailed, err
48
+ end
43
49
  end
44
50
 
45
51
  if loaded? && defined?(::Patron::Request::VALID_ACTIONS)
@@ -58,9 +64,5 @@ module Faraday
58
64
  session
59
65
  end
60
66
  end
61
-
62
- def prepend_proxy_auth_string(proxy, session)
63
- session.proxy.insert(7, "#{proxy[:user]}:#{proxy[:password]}@")
64
- end
65
67
  end
66
68
  end