faraday 0.12.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d6ed59b880f0d605e4efc8ce513ced0b4688c4ef
4
- data.tar.gz: 21d3a0326416db1e19add553b8d5c0ef1c5a3b50
2
+ SHA256:
3
+ metadata.gz: 3737507d92b48e4b98e891c5026210048ab70b2b1ee17ea20993b8ffc65eafb5
4
+ data.tar.gz: caaaa7f7ad1bdda848ac7bc70d4d1b9ba9d06bc85912a61b1ee4082086629a26
5
5
  SHA512:
6
- metadata.gz: 9e882a5072cc36a986e8fc2f23f0563755bae2d0c4e6fed4adc51a6fd46b449ea6be3dd189662ea0aa4f427c5402969bfb0ad626c837e9bad68d06aa0a3cc6d3
7
- data.tar.gz: 63856c35cbcceda6abeb3dc076574d7e880343e8ee7af5e80971c08637a647a079433b1220161c24871d3718c268d018ebdce009ca46d89c4da7c059fdaeaf72
6
+ metadata.gz: 231b96efdd5b0399f204edaeba22d01661b9d301aff6cad7d097c27c1e95f0a0a8adc9184e88f089862fa9db9a833e50a3eb7bfb96034a2df7f1bdd93661f3fb
7
+ data.tar.gz: f19fca5f59454b7b620160c3e58339e0dff8049be29bda8e8f1a494455dac6c62a1c6cbe4b4e141e999ea3df25cbcdf3ef09d65e93d71a882c318c75c3070dec
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2015 Rick Olson, Zack Hobson
1
+ Copyright (c) 2009-2017 Rick Olson, Zack Hobson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/faraday.svg)](https://rubygems.org/gems/faraday)
4
4
  [![Build Status](https://travis-ci.org/lostisland/faraday.svg)](https://travis-ci.org/lostisland/faraday)
5
+ [![Coverage Status](https://coveralls.io/repos/github/lostisland/faraday/badge.svg?branch=master)](https://coveralls.io/github/lostisland/faraday?branch=master)
6
+ [![Code Climate](https://codeclimate.com/github/lostisland/faraday/badges/gpa.svg)](https://codeclimate.com/github/lostisland/faraday)
5
7
  [![Gitter](https://badges.gitter.im/lostisland/faraday.svg)](https://gitter.im/lostisland/faraday?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
6
8
 
7
9
 
@@ -9,7 +11,7 @@ Faraday is an HTTP client lib that provides a common interface over many
9
11
  adapters (such as Net::HTTP) and embraces the concept of Rack middleware when
10
12
  processing the request/response cycle.
11
13
 
12
- Faraday supports these adapters:
14
+ Faraday supports these adapters out of the box:
13
15
 
14
16
  * [Net::HTTP][net_http] _(default)_
15
17
  * [Net::HTTP::Persistent][persistent]
@@ -18,6 +20,10 @@ Faraday supports these adapters:
18
20
  * [EventMachine][]
19
21
  * [HTTPClient][]
20
22
 
23
+ Adapters are slowly being moved into their own gems, or bundled with HTTP clients:
24
+
25
+ * [Typhoeus][]
26
+
21
27
  It also includes a Rack adapter for hitting loaded Rack applications through
22
28
  Rack::Test, and a Test adapter for stubbing requests by hand.
23
29
 
@@ -38,11 +44,12 @@ stack and default adapter (see [Faraday::RackBuilder#initialize](https://github.
38
44
  A more flexible way to use Faraday is to start with a Connection object. If you want to keep the same defaults, you can use this syntax:
39
45
 
40
46
  ```ruby
41
- conn = Faraday.new(:url => 'http://www.example.com')
42
- response = conn.get '/users' # GET http://www.example.com/users'
47
+ conn = Faraday.new(:url => 'http://www.example.com')
48
+ response = conn.get '/users' # GET http://www.example.com/users'
43
49
  ```
44
50
 
45
- Connections can also take an options hash as a parameter or be configured by using a block. Checkout the section called [Advanced middleware usage](#advanced-middleware-usage) for more details about how to use this block for configurations.
51
+ Connections can also take an options hash as a parameter or be configured by using a block. Checkout the section called [Advanced middleware usage](#advanced-middleware-usage) for more details about how to use this block for configurations.
52
+ Since the default middleware stack uses url\_encoded middleware and default adapter, use them on building your own middleware stack.
46
53
 
47
54
  ```ruby
48
55
  conn = Faraday.new(:url => 'http://sushi.com') do |faraday|
@@ -54,6 +61,7 @@ end
54
61
  # Filter sensitive information from logs with a regex matcher
55
62
 
56
63
  conn = Faraday.new(:url => 'http://sushi.com/api_key=s3cr3t') do |faraday|
64
+ faraday.request :url_encoded # form-encode POST params
57
65
  faraday.response :logger do | logger |
58
66
  logger.filter(/(api_key=)(\w+)/,'\1[REMOVED]')
59
67
  end
@@ -61,7 +69,7 @@ conn = Faraday.new(:url => 'http://sushi.com/api_key=s3cr3t') do |faraday|
61
69
  end
62
70
  ```
63
71
 
64
- Once you have the connection object, use it to make HTTP requests. You can pass paramters to it in a few different ways:
72
+ Once you have the connection object, use it to make HTTP requests. You can pass parameters to it in a few different ways:
65
73
 
66
74
  ```ruby
67
75
  ## GET ##
@@ -140,6 +148,36 @@ The value of Faraday `params_encoder` can be any object that responds to:
140
148
  The encoder will affect both how query strings are processed and how POST bodies
141
149
  get serialized. The default encoder is Faraday::NestedParamsEncoder.
142
150
 
151
+ ## Authentication
152
+
153
+ Basic and Token authentication are handled by Faraday::Request::BasicAuthentication and Faraday::Request::TokenAuthentication respectively. These can be added as middleware manually or through the helper methods.
154
+
155
+ ```ruby
156
+ Faraday.new(...) do |conn|
157
+ conn.basic_auth('username', 'password')
158
+ end
159
+
160
+ Faraday.new(...) do |conn|
161
+ conn.token_auth('authentication-token')
162
+ end
163
+ ```
164
+
165
+ ## Proxy
166
+
167
+ Faraday will try to automatically infer the proxy settings from your system using `URI#find_proxy`.
168
+ This will retrieve them from environment variables such as http_proxy, ftp_proxy, no_proxy, etc.
169
+ If for any reason you want to disable this behaviour, you can do so by setting the global varibale `ignore_env_proxy`:
170
+
171
+ ```ruby
172
+ Faraday.ignore_env_proxy = true
173
+ ```
174
+
175
+ You can also specify a custom proxy when initializing the connection
176
+
177
+ ```ruby
178
+ Faraday.new('http://www.example.com', :proxy => 'http://proxy.com')
179
+ ```
180
+
143
181
  ## Advanced middleware usage
144
182
 
145
183
  The order in which middleware is stacked is important. Like with Rack, the
@@ -152,6 +190,7 @@ Faraday.new(...) do |conn|
152
190
  conn.request :multipart
153
191
  conn.request :url_encoded
154
192
 
193
+ # Last middleware must be the adapter:
155
194
  conn.adapter :net_http
156
195
  end
157
196
  ```
@@ -216,7 +255,7 @@ later, response. Some keys are:
216
255
 
217
256
  ## Ad-hoc adapters customization
218
257
 
219
- Faraday is intended to be a generic interface between your code and the adapter. However, sometimes you need to access a feature specific to one of the adapters that is not covered in Faraday's interface.
258
+ Faraday is intended to be a generic interface between your code and the adapter. However, sometimes you need to access a feature specific to one of the adapters that is not covered in Faraday's interface.
220
259
 
221
260
  When that happens, you can pass a block when specifying the adapter to customize it. The block parameter will change based on the adapter you're using. See below for some examples.
222
261
 
@@ -296,11 +335,6 @@ resp = test.get '/else' #=> raises "no such stub" error
296
335
  stubs.verify_stubbed_calls
297
336
  ```
298
337
 
299
- ## TODO
300
-
301
- * support streaming requests/responses
302
- * better stubbing API
303
-
304
338
  ## Supported Ruby versions
305
339
 
306
340
  This library aims to support and is [tested against][travis] the following Ruby
@@ -323,9 +357,15 @@ implementation, you will be responsible for providing patches in a timely
323
357
  fashion. If critical issues for a particular implementation exist at the time
324
358
  of a major release, support for that Ruby version may be dropped.
325
359
 
360
+ ## Contribute
361
+
362
+ Do you want to contribute to Faraday?
363
+ Open the issues page and check for the `any volunteer?` label!
364
+ But before you start coding, please read our [Contributing Guide](https://github.com/lostisland/faraday/blob/master/.github/CONTRIBUTING.md)
365
+
326
366
  ## Copyright
327
367
 
328
- Copyright (c) 2009-2013 [Rick Olson](mailto:technoweenie@gmail.com), Zack Hobson.
368
+ Copyright (c) 2009-2017 [Rick Olson](mailto:technoweenie@gmail.com), Zack Hobson.
329
369
  See [LICENSE][] for details.
330
370
 
331
371
  [net_http]: http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/Net/HTTP.html
@@ -335,6 +375,7 @@ See [LICENSE][] for details.
335
375
  [patron]: http://toland.github.io/patron/
336
376
  [eventmachine]: https://github.com/igrigorik/em-http-request#readme
337
377
  [httpclient]: https://github.com/nahi/httpclient
378
+ [typhoeus]: https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb
338
379
  [jruby]: http://jruby.org/
339
380
  [rubinius]: http://rubini.us/
340
381
  [license]: LICENSE.md
@@ -30,7 +30,6 @@ module Faraday
30
30
 
31
31
  if req[:open_timeout]
32
32
  opts[:connect_timeout] = req[:open_timeout]
33
- opts[:write_timeout] = req[:open_timeout]
34
33
  end
35
34
 
36
35
  if req[:proxy]
@@ -50,7 +50,7 @@ module Faraday
50
50
  else
51
51
  raise Faraday::Error::ClientError, $!
52
52
  end
53
- rescue Errno::ECONNREFUSED, IOError
53
+ rescue Errno::ECONNREFUSED, IOError, SocketError
54
54
  raise Faraday::Error::ConnectionFailed, $!
55
55
  rescue => err
56
56
  if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
@@ -56,6 +56,8 @@ module Faraday
56
56
  raise Faraday::Error::TimeoutError, err
57
57
  end
58
58
 
59
+ private
60
+
59
61
  def create_request(env)
60
62
  request = Net::HTTPGenericRequest.new \
61
63
  env[:method].to_s.upcase, # request method
@@ -87,10 +89,10 @@ module Faraday
87
89
 
88
90
  def net_http_connection(env)
89
91
  if proxy = env[:request][:proxy]
90
- Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password])
92
+ Net::HTTP::Proxy(proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
91
93
  else
92
94
  Net::HTTP
93
- end.new(env[:url].host, env[:url].port || (env[:url].scheme == 'https' ? 443 : 80))
95
+ end.new(env[:url].hostname, env[:url].port || (env[:url].scheme == 'https' ? 443 : 80))
94
96
  end
95
97
 
96
98
  def configure_ssl(http, ssl)
@@ -1,13 +1,24 @@
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'
4
-
5
1
  module Faraday
6
2
  class Adapter
7
3
  class NetHttpPersistent < NetHttp
8
4
  dependency 'net/http/persistent'
9
5
 
6
+ private
7
+
10
8
  def net_http_connection(env)
9
+ proxy_uri = proxy_uri(env)
10
+
11
+ cached_connection env[:url], proxy_uri do
12
+ if Net::HTTP::Persistent.instance_method(:initialize).parameters.first == [:key, :name]
13
+ Net::HTTP::Persistent.new(name: 'Faraday', proxy: proxy_uri)
14
+ else
15
+ Net::HTTP::Persistent.new('Faraday', proxy_uri)
16
+ end
17
+ end
18
+ end
19
+
20
+ def proxy_uri(env)
21
+ proxy_uri = nil
11
22
  if (proxy = env[:request][:proxy])
12
23
  proxy_uri = ::URI::HTTP === proxy[:uri] ? proxy[:uri].dup : ::URI.parse(proxy[:uri].to_s)
13
24
  proxy_uri.user = proxy_uri.password = nil
@@ -16,10 +27,8 @@ module Faraday
16
27
  define_method(:user) { proxy[:user] }
17
28
  define_method(:password) { proxy[:password] }
18
29
  end if proxy[:user]
19
- return Net::HTTP::Persistent.new 'Faraday', proxy_uri
20
30
  end
21
-
22
- Net::HTTP::Persistent.new 'Faraday'
31
+ proxy_uri
23
32
  end
24
33
 
25
34
  def perform_request(http, env)
@@ -45,6 +54,10 @@ module Faraday
45
54
  http.ca_file = ssl[:ca_file] if ssl[:ca_file]
46
55
  http.ssl_version = ssl[:version] if ssl[:version]
47
56
  end
57
+
58
+ def cached_connection(url, proxy_uri)
59
+ (@cached_connection ||= {})[[url.scheme, url.host, url.port, proxy_uri]] ||= yield
60
+ end
48
61
  end
49
62
  end
50
63
  end
@@ -5,11 +5,11 @@ module Faraday
5
5
 
6
6
  def call(env)
7
7
  super
8
-
9
8
  # TODO: support streaming requests
10
9
  env[:body] = env[:body].read if env[:body].respond_to? :read
11
10
 
12
11
  session = @session ||= create_session
12
+ configure_ssl(session, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl]
13
13
 
14
14
  if req = env[:request]
15
15
  session.timeout = session.connect_timeout = req[:timeout] if req[:timeout]
@@ -37,7 +37,7 @@ module Faraday
37
37
 
38
38
  @app.call env
39
39
  rescue ::Patron::TimeoutError => err
40
- if err.message == "Connection time-out"
40
+ if connection_timed_out_message?(err.message)
41
41
  raise Faraday::Error::ConnectionFailed, err
42
42
  else
43
43
  raise Faraday::Error::TimeoutError, err
@@ -67,10 +67,34 @@ module Faraday
67
67
 
68
68
  def create_session
69
69
  session = ::Patron::Session.new
70
- session.insecure = true
71
70
  @config_block.call(session) if @config_block
72
71
  session
73
72
  end
73
+
74
+ def configure_ssl(session, ssl)
75
+ if ssl.fetch(:verify, true)
76
+ session.cacert = ssl[:ca_file]
77
+ else
78
+ session.insecure = true
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ CURL_TIMEOUT_MESSAGES = [ "Connection time-out",
85
+ "Connection timed out",
86
+ "Timed out before name resolve",
87
+ "server connect has timed out",
88
+ "Resolving timed out",
89
+ "name lookup timed out",
90
+ "timed out before SSL",
91
+ "connect() timed out"
92
+ ].freeze
93
+
94
+ def connection_timed_out_message?(message)
95
+ CURL_TIMEOUT_MESSAGES.any? { |curl_message| message.include?(curl_message) }
96
+ end
97
+
74
98
  end
75
99
  end
76
100
  end
@@ -53,17 +53,17 @@ module Faraday
53
53
  @stack.empty?
54
54
  end
55
55
 
56
- def match(request_method, path, headers, body)
56
+ def match(request_method, host, path, headers, body)
57
57
  return false if !@stack.key?(request_method)
58
58
  stack = @stack[request_method]
59
59
  consumed = (@consumed[request_method] ||= [])
60
60
 
61
- stub, meta = matches?(stack, path, headers, body)
61
+ stub, meta = matches?(stack, host, path, headers, body)
62
62
  if stub
63
63
  consumed << stack.delete(stub)
64
64
  return stub, meta
65
65
  end
66
- matches?(consumed, path, headers, body)
66
+ matches?(consumed, host, path, headers, body)
67
67
  end
68
68
 
69
69
  def get(path, headers = {}, &block)
@@ -110,29 +110,35 @@ module Faraday
110
110
  protected
111
111
 
112
112
  def new_stub(request_method, path, headers = {}, body=nil, &block)
113
- normalized_path = path.is_a?(Regexp) ? path : Faraday::Utils.normalize_path(path)
114
- (@stack[request_method] ||= []) << Stub.new(normalized_path, headers, body, block)
113
+ normalized_path, host =
114
+ if path.is_a?(Regexp)
115
+ path
116
+ else
117
+ [Faraday::Utils.normalize_path(path), Faraday::Utils.URI(path).host]
118
+ end
119
+
120
+ (@stack[request_method] ||= []) << Stub.new(host, normalized_path, headers, body, block)
115
121
  end
116
122
 
117
- def matches?(stack, path, headers, body)
123
+ def matches?(stack, host, path, headers, body)
118
124
  stack.each do |stub|
119
- match_result, meta = stub.matches?(path, headers, body)
125
+ match_result, meta = stub.matches?(host, path, headers, body)
120
126
  return stub, meta if match_result
121
127
  end
122
128
  nil
123
129
  end
124
130
  end
125
131
 
126
- class Stub < Struct.new(:path, :params, :headers, :body, :block)
127
- def initialize(full, headers, body, block)
132
+ class Stub < Struct.new(:host, :path, :params, :headers, :body, :block)
133
+ def initialize(host, full, headers, body, block)
128
134
  path, query = full.respond_to?(:split) ? full.split("?") : full
129
135
  params = query ?
130
136
  Faraday::Utils.parse_nested_query(query) :
131
137
  {}
132
- super(path, params, headers, body, block)
138
+ super(host, path, params, headers, body, block)
133
139
  end
134
140
 
135
- def matches?(request_uri, request_headers, request_body)
141
+ def matches?(request_host, request_uri, request_headers, request_body)
136
142
  request_path, request_query = request_uri.split('?')
137
143
  request_params = request_query ?
138
144
  Faraday::Utils.parse_nested_query(request_query) :
@@ -140,7 +146,8 @@ module Faraday
140
146
  # meta is a hash use as carrier
141
147
  # that will be yielded to consumer block
142
148
  meta = {}
143
- return path_match?(request_path, meta) &&
149
+ return (host.nil? || host == request_host) &&
150
+ path_match?(request_path, meta) &&
144
151
  params_match?(request_params) &&
145
152
  (body.to_s.size.zero? || request_body == body) &&
146
153
  headers_match?(request_headers), meta
@@ -183,10 +190,11 @@ module Faraday
183
190
 
184
191
  def call(env)
185
192
  super
193
+ host = env[:url].host
186
194
  normalized_path = Faraday::Utils.normalize_path(env[:url])
187
195
  params_encoder = env.request.params_encoder || Faraday::Utils.default_params_encoder
188
196
 
189
- stub, meta = stubs.match(env[:method], normalized_path, env.request_headers, env[:body])
197
+ stub, meta = stubs.match(env[:method], host, normalized_path, env.request_headers, env[:body])
190
198
  if stub
191
199
  env[:params] = (query = env[:url].query) ?
192
200
  params_encoder.decode(query) : {}
@@ -1,123 +1,12 @@
1
1
  module Faraday
2
2
  class Adapter
3
+ # This class is just a stub, the real adapter is in https://github.com/philsturgeon/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb
3
4
  class Typhoeus < Faraday::Adapter
4
- self.supports_parallel = true
5
-
6
- def self.setup_parallel_manager(options = {})
7
- options.empty? ? ::Typhoeus::Hydra.hydra : ::Typhoeus::Hydra.new(options)
8
- end
5
+ # Needs to define this method in order to support Typhoeus <= 1.3.0
6
+ def call; end
9
7
 
10
8
  dependency 'typhoeus'
11
-
12
- def call(env)
13
- super
14
- perform_request env
15
- @app.call env
16
- end
17
-
18
- def perform_request(env)
19
- read_body env
20
-
21
- hydra = env[:parallel_manager] || self.class.setup_parallel_manager
22
- hydra.queue request(env)
23
- hydra.run unless parallel?(env)
24
- rescue Errno::ECONNREFUSED
25
- raise Error::ConnectionFailed, $!
26
- end
27
-
28
- # TODO: support streaming requests
29
- def read_body(env)
30
- env[:body] = env[:body].read if env[:body].respond_to? :read
31
- end
32
-
33
- def request(env)
34
- method = env[:method]
35
- # For some reason, prevents Typhoeus from using "100-continue".
36
- # We want this because Webrick 1.3.1 can't seem to handle it w/ PUT.
37
- method = method.to_s.upcase if method == :put
38
-
39
- req = ::Typhoeus::Request.new env[:url].to_s,
40
- :method => method,
41
- :body => env[:body],
42
- :headers => env[:request_headers],
43
- :disable_ssl_peer_verification => (env[:ssl] && env[:ssl].disable?)
44
-
45
- configure_ssl req, env
46
- configure_proxy req, env
47
- configure_timeout req, env
48
- configure_socket req, env
49
-
50
- req.on_complete do |resp|
51
- if resp.timed_out?
52
- if parallel?(env)
53
- # TODO: error callback in async mode
54
- else
55
- raise Faraday::Error::TimeoutError, "request timed out"
56
- end
57
- end
58
-
59
- case resp.curl_return_code
60
- when 0
61
- # everything OK
62
- when 7
63
- raise Error::ConnectionFailed, resp.curl_error_message
64
- when 60
65
- raise Faraday::SSLError, resp.curl_error_message
66
- else
67
- raise Error::ClientError, resp.curl_error_message
68
- end
69
-
70
- save_response(env, resp.code, resp.body) do |response_headers|
71
- response_headers.parse resp.headers
72
- end
73
- # in async mode, :response is initialized at this point
74
- env[:response].finish(env) if parallel?(env)
75
- end
76
-
77
- req
78
- end
79
-
80
- def configure_ssl(req, env)
81
- ssl = env[:ssl]
82
-
83
- req.ssl_version = ssl[:version] if ssl[:version]
84
- req.ssl_cert = ssl[:client_cert] if ssl[:client_cert]
85
- req.ssl_key = ssl[:client_key] if ssl[:client_key]
86
- req.ssl_cacert = ssl[:ca_file] if ssl[:ca_file]
87
- req.ssl_capath = ssl[:ca_path] if ssl[:ca_path]
88
- end
89
-
90
- def configure_proxy(req, env)
91
- proxy = request_options(env)[:proxy]
92
- return unless proxy
93
-
94
- req.proxy = "#{proxy[:uri].host}:#{proxy[:uri].port}"
95
-
96
- if proxy[:user] && proxy[:password]
97
- req.proxy_username = proxy[:user]
98
- req.proxy_password = proxy[:password]
99
- end
100
- end
101
-
102
- def configure_timeout(req, env)
103
- env_req = request_options(env)
104
- req.timeout = req.connect_timeout = (env_req[:timeout] * 1000) if env_req[:timeout]
105
- req.connect_timeout = (env_req[:open_timeout] * 1000) if env_req[:open_timeout]
106
- end
107
-
108
- def configure_socket(req, env)
109
- if bind = request_options(env)[:bind]
110
- req.interface = bind[:host]
111
- end
112
- end
113
-
114
- def request_options(env)
115
- env[:request]
116
- end
117
-
118
- def parallel?(env)
119
- !!env[:parallel_manager]
120
- end
9
+ dependency 'typhoeus/adapters/faraday'
121
10
  end
122
11
  end
123
12
  end
@@ -40,6 +40,8 @@ module Faraday
40
40
  env.clear_body if env.needs_body?
41
41
  end
42
42
 
43
+ private
44
+
43
45
  def save_response(env, status, body, headers = nil, reason_phrase = nil)
44
46
  env.status = status
45
47
  env.body = body
@@ -53,9 +53,9 @@ module Faraday
53
53
  autoload_all 'faraday/adapter',
54
54
  :NetHttp => 'net_http',
55
55
  :NetHttpPersistent => 'net_http_persistent',
56
- :Typhoeus => 'typhoeus',
57
56
  :EMSynchrony => 'em_synchrony',
58
57
  :EMHttp => 'em_http',
58
+ :Typhoeus => 'typhoeus',
59
59
  :Patron => 'patron',
60
60
  :Excon => 'excon',
61
61
  :Test => 'test',
@@ -39,6 +39,9 @@ module Faraday
39
39
  # Public: Sets the default parallel manager for this connection.
40
40
  attr_writer :default_parallel_manager
41
41
 
42
+ # Public: Gets or Sets the Hash proxy options.
43
+ # attr_reader :proxy
44
+
42
45
  # Public: Initializes a new Faraday::Connection.
43
46
  #
44
47
  # url - URI or String base URL to use as a prefix for all
@@ -57,7 +60,7 @@ module Faraday
57
60
  def initialize(url = nil, options = nil)
58
61
  options = ConnectionOptions.from(options)
59
62
 
60
- if url.is_a?(Hash)
63
+ if url.is_a?(Hash) || url.is_a?(ConnectionOptions)
61
64
  options = options.merge(url)
62
65
  url = options.url
63
66
  end
@@ -79,23 +82,9 @@ module Faraday
79
82
  @params.update(options.params) if options.params
80
83
  @headers.update(options.headers) if options.headers
81
84
 
82
- @proxy = nil
83
- proxy(options.fetch(:proxy) {
84
- uri = nil
85
- if URI.parse("").respond_to?(:find_proxy)
86
- case url
87
- when String
88
- uri = URI.parse(url).find_proxy
89
- when URI
90
- uri = url.find_proxy
91
- when nil
92
- uri = find_default_proxy
93
- end
94
- else
95
- uri = find_default_proxy
96
- end
97
- uri
98
- })
85
+ @manual_proxy = !!options.proxy
86
+ @proxy = options.proxy ? ProxyOptions.from(options.proxy) : proxy_from_env(url)
87
+ @temp_proxy = @proxy
99
88
 
100
89
  yield(self) if block_given?
101
90
 
@@ -292,9 +281,17 @@ module Faraday
292
281
  # Public: Gets or Sets the Hash proxy options.
293
282
  def proxy(arg = nil)
294
283
  return @proxy if arg.nil?
284
+ warn 'Warning: use of proxy(new_value) to set connection proxy have been DEPRECATED and will be removed in Faraday 1.0'
285
+ @manual_proxy = true
295
286
  @proxy = ProxyOptions.from(arg)
296
287
  end
297
288
 
289
+ # Public: Sets the Hash proxy options.
290
+ def proxy=(new_value)
291
+ @manual_proxy = true
292
+ @proxy = new_value ? ProxyOptions.from(new_value) : nil
293
+ end
294
+
298
295
  def_delegators :url_prefix, :scheme, :scheme=, :host, :host=, :port, :port=
299
296
  def_delegator :url_prefix, :path, :path_prefix
300
297
 
@@ -313,7 +310,7 @@ module Faraday
313
310
  #
314
311
  # conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri
315
312
  #
316
- # Returns the parsed URI from teh given input..
313
+ # Returns the parsed URI from the given input..
317
314
  def url_prefix=(url, encoder = nil)
318
315
  uri = @url_prefix = Utils.URI(url)
319
316
  self.path_prefix = uri.path
@@ -376,7 +373,11 @@ module Faraday
376
373
  raise ArgumentError, "unknown http method: #{method}"
377
374
  end
378
375
 
376
+ # Resets temp_proxy
377
+ @temp_proxy = proxy_for_request(url)
378
+
379
379
  request = build_request(method) do |req|
380
+ req.options = req.options.merge(:proxy => @temp_proxy)
380
381
  req.url(url) if url
381
382
  req.headers.update(headers) if headers
382
383
  req.body = body if body
@@ -393,7 +394,7 @@ module Faraday
393
394
  Request.create(method) do |req|
394
395
  req.params = self.params.dup
395
396
  req.headers = self.headers.dup
396
- req.options = self.options.merge(:proxy => self.proxy)
397
+ req.options = self.options
397
398
  yield(req) if block_given?
398
399
  end
399
400
  end
@@ -443,13 +444,41 @@ module Faraday
443
444
  headers[Faraday::Request::Authorization::KEY] = header
444
445
  end
445
446
 
447
+ def proxy_from_env(url)
448
+ return if Faraday.ignore_env_proxy
449
+ uri = nil
450
+ if URI.parse('').respond_to?(:find_proxy)
451
+ case url
452
+ when String
453
+ uri = Utils.URI(url)
454
+ uri = URI.parse("#{uri.scheme}://#{uri.hostname}").find_proxy
455
+ when URI
456
+ uri = url.find_proxy
457
+ when nil
458
+ uri = find_default_proxy
459
+ end
460
+ else
461
+ warn 'no_proxy is unsupported' if ENV['no_proxy'] || ENV['NO_PROXY']
462
+ uri = find_default_proxy
463
+ end
464
+ ProxyOptions.from(uri) if uri
465
+ end
466
+
446
467
  def find_default_proxy
447
- warn 'no_proxy is unsupported' if ENV['no_proxy'] || ENV['NO_PROXY']
448
468
  uri = ENV['http_proxy']
449
469
  if uri && !uri.empty?
450
470
  uri = 'http://' + uri if uri !~ /^http/i
451
471
  uri
452
472
  end
453
473
  end
474
+
475
+ def proxy_for_request(url)
476
+ return self.proxy if @manual_proxy
477
+ if url && Utils.URI(url).absolute?
478
+ proxy_from_env(url)
479
+ else
480
+ self.proxy
481
+ end
482
+ end
454
483
  end
455
484
  end
data/lib/faraday/error.rb CHANGED
@@ -1,9 +1,8 @@
1
1
  module Faraday
2
2
  class Error < StandardError; end
3
- class MissingDependency < Error; end
4
3
 
5
4
  class ClientError < Error
6
- attr_reader :response
5
+ attr_reader :response, :wrapped_exception
7
6
 
8
7
  def initialize(ex, response = nil)
9
8
  @wrapped_exception = nil
@@ -56,8 +55,12 @@ module Faraday
56
55
  class SSLError < ClientError
57
56
  end
58
57
 
59
- [:MissingDependency, :ClientError, :ConnectionFailed, :ResourceNotFound,
60
- :ParsingError, :TimeoutError, :SSLError].each do |const|
58
+ class RetriableResponse < ClientError; end
59
+
60
+ [:ClientError, :ConnectionFailed, :ResourceNotFound,
61
+ :ParsingError, :TimeoutError, :SSLError, :RetriableResponse].each do |const|
61
62
  Error.const_set(const, Faraday.const_get(const))
62
63
  end
64
+
65
+
63
66
  end
@@ -49,7 +49,7 @@ module Faraday
49
49
  other.each do |key, other_value|
50
50
  self_value = self.send(key)
51
51
  sub_options = self.class.options_for(key)
52
- new_value = sub_options ? self_value.merge(other_value) : other_value
52
+ new_value = (self_value && sub_options && other_value) ? self_value.merge(other_value) : other_value
53
53
  self.send("#{key}=", new_value) unless new_value.nil?
54
54
  end
55
55
  self
@@ -243,8 +243,8 @@ module Faraday
243
243
  super(value)
244
244
  end
245
245
 
246
- memoized(:user) { uri.user && Utils.unescape(uri.user) }
247
- memoized(:password) { uri.password && Utils.unescape(uri.password) }
246
+ memoized(:user) { uri && uri.user && Utils.unescape(uri.user) }
247
+ memoized(:password) { uri && uri.password && Utils.unescape(uri.password) }
248
248
  end
249
249
 
250
250
  class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url,
@@ -84,6 +84,7 @@ module Faraday
84
84
  use_symbol(Faraday::Middleware, klass, *args, &block)
85
85
  else
86
86
  raise_if_locked
87
+ warn_middleware_after_adapter if adapter_set?
87
88
  @handlers << self.class::Handler.new(klass, *args, &block)
88
89
  end
89
90
  end
@@ -105,6 +106,7 @@ module Faraday
105
106
  def insert(index, *args, &block)
106
107
  raise_if_locked
107
108
  index = assert_index(index)
109
+ warn_middleware_after_adapter if inserting_after_adapter?(index)
108
110
  handler = self.class::Handler.new(*args, &block)
109
111
  @handlers.insert(index, handler)
110
112
  end
@@ -136,6 +138,8 @@ module Faraday
136
138
  #
137
139
  # Returns a Faraday::Response.
138
140
  def build_response(connection, request)
141
+ warn 'WARNING: No adapter was configured for this request' unless adapter_set?
142
+
139
143
  app.call(build_env(connection, request))
140
144
  end
141
145
 
@@ -200,6 +204,26 @@ module Faraday
200
204
  raise StackLocked, "can't modify middleware stack after making a request" if locked?
201
205
  end
202
206
 
207
+ def warn_middleware_after_adapter
208
+ warn "WARNING: Unexpected middleware set after the adapter. " \
209
+ "This won't be supported from Faraday 1.0."
210
+ end
211
+
212
+ def adapter_set?
213
+ @handlers.any? { |handler| is_adapter?(handler) }
214
+ end
215
+
216
+ def inserting_after_adapter?(index)
217
+ adapter_index = @handlers.find_index { |handler| is_adapter?(handler) }
218
+ return false if adapter_index.nil?
219
+
220
+ index > adapter_index
221
+ end
222
+
223
+ def is_adapter?(handler)
224
+ handler.klass.ancestors.include? Faraday::Adapter
225
+ end
226
+
203
227
  def use_symbol(mod, key, *args, &block)
204
228
  use(mod.lookup_middleware(key), *args, &block)
205
229
  end
@@ -23,7 +23,9 @@ module Faraday
23
23
  IDEMPOTENT_METHODS = [:delete, :get, :head, :options, :put]
24
24
 
25
25
  class Options < Faraday::Options.new(:max, :interval, :max_interval, :interval_randomness,
26
- :backoff_factor, :exceptions, :methods, :retry_if)
26
+ :backoff_factor, :exceptions, :methods, :retry_if, :retry_block,
27
+ :retry_statuses)
28
+
27
29
  DEFAULT_CHECK = lambda { |env,exception| false }
28
30
 
29
31
  def self.from(value)
@@ -56,7 +58,8 @@ module Faraday
56
58
 
57
59
  def exceptions
58
60
  Array(self[:exceptions] ||= [Errno::ETIMEDOUT, 'Timeout::Error',
59
- Error::TimeoutError])
61
+ Error::TimeoutError,
62
+ Faraday::Error::RetriableResponse])
60
63
  end
61
64
 
62
65
  def methods
@@ -67,6 +70,13 @@ module Faraday
67
70
  self[:retry_if] ||= DEFAULT_CHECK
68
71
  end
69
72
 
73
+ def retry_block
74
+ self[:retry_block] ||= Proc.new {}
75
+ end
76
+
77
+ def retry_statuses
78
+ Array(self[:retry_statuses] ||= [])
79
+ end
70
80
  end
71
81
 
72
82
  # Public: Initialize middleware
@@ -83,8 +93,8 @@ module Faraday
83
93
  # (default: 1)
84
94
  # exceptions - The list of exceptions to handle. Exceptions can be
85
95
  # given as Class, Module, or String. (default:
86
- # [Errno::ETIMEDOUT, Timeout::Error,
87
- # Error::TimeoutError])
96
+ # [Errno::ETIMEDOUT, 'Timeout::Error',
97
+ # Error::TimeoutError, Faraday::Error::RetriableResponse])
88
98
  # methods - A list of HTTP methods to retry without calling retry_if. Pass
89
99
  # an empty Array to call retry_if for all exceptions.
90
100
  # (defaults to the idempotent HTTP methods in IDEMPOTENT_METHODS)
@@ -94,18 +104,21 @@ module Faraday
94
104
  # if the exception produced is non-recoverable or if the
95
105
  # the HTTP method called is not idempotent.
96
106
  # (defaults to return false)
107
+ # retry_block - block that is executed after every retry. Request environment, middleware options,
108
+ # current number of retries and the exception is passed to the block as parameters.
97
109
  def initialize(app, options = nil)
98
110
  super(app)
99
111
  @options = Options.from(options)
100
112
  @errmatch = build_exception_matcher(@options.exceptions)
101
113
  end
102
114
 
103
- def sleep_amount(retries)
104
- retry_index = @options.max - retries
105
- current_interval = @options.interval * (@options.backoff_factor ** retry_index)
106
- current_interval = [current_interval, @options.max_interval].min
107
- random_interval = rand * @options.interval_randomness.to_f * @options.interval
108
- current_interval + random_interval
115
+ def calculate_sleep_amount(retries, env)
116
+ retry_after = calculate_retry_after(env)
117
+ retry_interval = calculate_retry_interval(retries)
118
+
119
+ return if retry_after && retry_after > @options.max_interval
120
+
121
+ retry_after && retry_after >= retry_interval ? retry_after : retry_interval
109
122
  end
110
123
 
111
124
  def call(env)
@@ -113,15 +126,25 @@ module Faraday
113
126
  request_body = env[:body]
114
127
  begin
115
128
  env[:body] = request_body # after failure env[:body] is set to the response body
116
- @app.call(env)
129
+ @app.call(env).tap do |resp|
130
+ raise Faraday::Error::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status)
131
+ end
117
132
  rescue @errmatch => exception
118
133
  if retries > 0 && retry_request?(env, exception)
119
134
  retries -= 1
120
135
  rewind_files(request_body)
121
- sleep sleep_amount(retries + 1)
122
- retry
136
+ @options.retry_block.call(env, @options, retries, exception)
137
+ if (sleep_amount = calculate_sleep_amount(retries + 1, env))
138
+ sleep sleep_amount
139
+ retry
140
+ end
141
+ end
142
+
143
+ if exception.is_a?(Faraday::Error::RetriableResponse)
144
+ exception.response
145
+ else
146
+ raise
123
147
  end
124
- raise
125
148
  end
126
149
  end
127
150
 
@@ -151,13 +174,38 @@ module Faraday
151
174
  @options.methods.include?(env[:method]) || @options.retry_if.call(env, exception)
152
175
  end
153
176
 
154
- def rewind_files(env)
155
- env && env.each do |_, value|
177
+ def rewind_files(body)
178
+ return unless body.is_a?(Hash)
179
+ body.each do |_, value|
156
180
  if value.is_a? UploadIO
157
181
  value.rewind
158
182
  end
159
183
  end
160
184
  end
161
185
 
186
+ # MDN spec for Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
187
+ def calculate_retry_after(env)
188
+ response_headers = env[:response_headers]
189
+ return unless response_headers
190
+
191
+ retry_after_value = env[:response_headers]["Retry-After"]
192
+
193
+ # Try to parse date from the header value
194
+ begin
195
+ datetime = DateTime.rfc2822(retry_after_value)
196
+ datetime.to_time - Time.now.utc
197
+ rescue ArgumentError
198
+ retry_after_value.to_f
199
+ end
200
+ end
201
+
202
+ def calculate_retry_interval(retries)
203
+ retry_index = @options.max - retries
204
+ current_interval = @options.interval * (@options.backoff_factor ** retry_index)
205
+ current_interval = [current_interval, @options.max_interval].min
206
+ random_interval = rand * @options.interval_randomness.to_f * @options.interval
207
+
208
+ current_interval + random_interval
209
+ end
162
210
  end
163
211
  end
@@ -20,14 +20,14 @@ module Faraday
20
20
  def_delegators :@logger, :debug, :info, :warn, :error, :fatal
21
21
 
22
22
  def call(env)
23
- info "#{env.method} #{apply_filters(env.url.to_s)}"
23
+ info('request') { "#{env.method.upcase} #{apply_filters(env.url.to_s)}" }
24
24
  debug('request') { apply_filters( dump_headers env.request_headers ) } if log_headers?(:request)
25
25
  debug('request') { apply_filters( dump_body(env[:body]) ) } if env[:body] && log_body?(:request)
26
26
  super
27
27
  end
28
28
 
29
29
  def on_complete(env)
30
- info('Status') { env.status.to_s }
30
+ info('response') { "Status #{env.status.to_s}" }
31
31
  debug('response') { apply_filters( dump_headers env.response_headers ) } if log_headers?(:response)
32
32
  debug('response') { apply_filters( dump_body env[:body] ) } if env[:body] && log_body?(:response)
33
33
  end
data/lib/faraday/utils.rb CHANGED
@@ -10,12 +10,22 @@ module Faraday
10
10
  new(value)
11
11
  end
12
12
 
13
+ def self.allocate
14
+ new_self = super
15
+ new_self.initialize_names
16
+ new_self
17
+ end
18
+
13
19
  def initialize(hash = nil)
14
20
  super()
15
21
  @names = {}
16
22
  self.update(hash || {})
17
23
  end
18
24
 
25
+ def initialize_names
26
+ @names = {}
27
+ end
28
+
19
29
  # on dup/clone, we need to duplicate @names hash
20
30
  def initialize_copy(other)
21
31
  super
@@ -95,7 +105,14 @@ module Faraday
95
105
 
96
106
  def parse(header_string)
97
107
  return unless header_string && !header_string.empty?
98
- header_string.split(/\r\n/).
108
+
109
+ headers = header_string.split(/\r\n/)
110
+
111
+ # Find the last set of response headers.
112
+ start_index = headers.rindex { |x| x.match(/^HTTP\//) } || 0
113
+ last_response = headers.slice(start_index, headers.size)
114
+
115
+ last_response.
99
116
  tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line
100
117
  map { |h| h.split(/:\s*/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines
101
118
  each { |key, value|
@@ -108,14 +125,6 @@ module Faraday
108
125
  }
109
126
  end
110
127
 
111
- def init_with(coder)
112
- @names = coder['names']
113
- end
114
-
115
- def encode_with(coder)
116
- coder['names'] = @names
117
- end
118
-
119
128
  protected
120
129
 
121
130
  def names
data/lib/faraday.rb CHANGED
@@ -14,7 +14,7 @@ require 'forwardable'
14
14
  # conn.get '/'
15
15
  #
16
16
  module Faraday
17
- VERSION = "0.12.0"
17
+ VERSION = "0.15.0"
18
18
 
19
19
  class << self
20
20
  # Public: Gets or sets the root path that Faraday is being loaded from.
@@ -34,6 +34,9 @@ module Faraday
34
34
  # Faraday.get "https://faraday.com"
35
35
  attr_writer :default_connection
36
36
 
37
+ # Public: Tells faraday to ignore the environment proxy (http_proxy).
38
+ attr_accessor :ignore_env_proxy
39
+
37
40
  # Public: Initializes a new Faraday::Connection.
38
41
  #
39
42
  # url - The optional String base URL to use as a prefix for all
@@ -101,6 +104,7 @@ module Faraday
101
104
  end
102
105
  end
103
106
 
107
+ self.ignore_env_proxy = false
104
108
  self.root_path = File.expand_path "..", __FILE__
105
109
  self.lib_path = File.expand_path "../faraday", __FILE__
106
110
  self.default_adapter = :net_http
@@ -109,7 +113,7 @@ module Faraday
109
113
  #
110
114
  # Returns a Faraday::Connection, configured with the #default_adapter.
111
115
  def self.default_connection
112
- @default_connection ||= Connection.new
116
+ @default_connection ||= Connection.new(default_connection_options)
113
117
  end
114
118
 
115
119
  # Gets the default connection options used when calling Faraday#new.
@@ -121,6 +125,7 @@ module Faraday
121
125
 
122
126
  # Public: Sets the default options used when calling Faraday#new.
123
127
  def self.default_connection_options=(options)
128
+ @default_connection = nil
124
129
  @default_connection_options = ConnectionOptions.from(options)
125
130
  end
126
131
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faraday
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Olson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-01 00:00:00.000000000 Z
11
+ date: 2018-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multipart-post
@@ -92,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
92
  version: '0'
93
93
  requirements: []
94
94
  rubyforge_project:
95
- rubygems_version: 2.4.5
95
+ rubygems_version: 2.7.6
96
96
  signing_key:
97
97
  specification_version: 4
98
98
  summary: HTTP/REST API client library.