faraday 0.12.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.