faraday 0.7.4 → 1.0.1
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 +7 -0
- data/CHANGELOG.md +276 -0
- data/LICENSE.md +1 -1
- data/README.md +40 -153
- data/Rakefile +4 -139
- data/examples/client_spec.rb +65 -0
- data/examples/client_test.rb +79 -0
- data/lib/faraday/adapter/em_http.rb +286 -0
- data/lib/faraday/adapter/em_http_ssl_patch.rb +62 -0
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +69 -0
- data/lib/faraday/adapter/em_synchrony.rb +120 -36
- data/lib/faraday/adapter/excon.rb +108 -12
- data/lib/faraday/adapter/httpclient.rb +152 -0
- data/lib/faraday/adapter/net_http.rb +187 -43
- data/lib/faraday/adapter/net_http_persistent.rb +91 -0
- data/lib/faraday/adapter/patron.rb +106 -10
- data/lib/faraday/adapter/rack.rb +75 -0
- data/lib/faraday/adapter/test.rb +160 -61
- data/lib/faraday/adapter/typhoeus.rb +7 -46
- data/lib/faraday/adapter.rb +105 -33
- data/lib/faraday/adapter_registry.rb +30 -0
- data/lib/faraday/autoload.rb +95 -0
- data/lib/faraday/connection.rb +525 -157
- data/lib/faraday/dependency_loader.rb +37 -0
- data/lib/faraday/encoders/flat_params_encoder.rb +98 -0
- data/lib/faraday/encoders/nested_params_encoder.rb +171 -0
- data/lib/faraday/error.rb +122 -30
- data/lib/faraday/file_part.rb +128 -0
- data/lib/faraday/logging/formatter.rb +105 -0
- data/lib/faraday/middleware.rb +14 -22
- data/lib/faraday/middleware_registry.rb +129 -0
- data/lib/faraday/options/connection_options.rb +22 -0
- data/lib/faraday/options/env.rb +181 -0
- data/lib/faraday/options/proxy_options.rb +28 -0
- data/lib/faraday/options/request_options.rb +22 -0
- data/lib/faraday/options/ssl_options.rb +59 -0
- data/lib/faraday/options.rb +222 -0
- data/lib/faraday/param_part.rb +53 -0
- data/lib/faraday/parameters.rb +5 -0
- data/lib/faraday/rack_builder.rb +248 -0
- data/lib/faraday/request/authorization.rb +55 -0
- data/lib/faraday/request/basic_authentication.rb +20 -0
- data/lib/faraday/request/instrumentation.rb +54 -0
- data/lib/faraday/request/multipart.rb +84 -48
- data/lib/faraday/request/retry.rb +239 -0
- data/lib/faraday/request/token_authentication.rb +20 -0
- data/lib/faraday/request/url_encoded.rb +46 -27
- data/lib/faraday/request.rb +112 -50
- data/lib/faraday/response/logger.rb +24 -25
- data/lib/faraday/response/raise_error.rb +40 -11
- data/lib/faraday/response.rb +44 -35
- data/lib/faraday/utils/headers.rb +139 -0
- data/lib/faraday/utils/params_hash.rb +61 -0
- data/lib/faraday/utils.rb +72 -117
- data/lib/faraday.rb +142 -64
- data/spec/external_adapters/faraday_specs_setup.rb +14 -0
- data/spec/faraday/adapter/em_http_spec.rb +47 -0
- data/spec/faraday/adapter/em_synchrony_spec.rb +16 -0
- data/spec/faraday/adapter/excon_spec.rb +49 -0
- data/spec/faraday/adapter/httpclient_spec.rb +73 -0
- data/spec/faraday/adapter/net_http_persistent_spec.rb +57 -0
- data/spec/faraday/adapter/net_http_spec.rb +64 -0
- data/spec/faraday/adapter/patron_spec.rb +18 -0
- data/spec/faraday/adapter/rack_spec.rb +8 -0
- data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
- data/spec/faraday/adapter_registry_spec.rb +28 -0
- data/spec/faraday/adapter_spec.rb +55 -0
- data/spec/faraday/composite_read_io_spec.rb +80 -0
- data/spec/faraday/connection_spec.rb +691 -0
- data/spec/faraday/error_spec.rb +45 -0
- data/spec/faraday/middleware_spec.rb +26 -0
- data/spec/faraday/options/env_spec.rb +70 -0
- data/spec/faraday/options/options_spec.rb +297 -0
- data/spec/faraday/options/proxy_options_spec.rb +37 -0
- data/spec/faraday/options/request_options_spec.rb +19 -0
- data/spec/faraday/params_encoders/flat_spec.rb +34 -0
- data/spec/faraday/params_encoders/nested_spec.rb +134 -0
- data/spec/faraday/rack_builder_spec.rb +196 -0
- data/spec/faraday/request/authorization_spec.rb +88 -0
- data/spec/faraday/request/instrumentation_spec.rb +76 -0
- data/spec/faraday/request/multipart_spec.rb +274 -0
- data/spec/faraday/request/retry_spec.rb +242 -0
- data/spec/faraday/request/url_encoded_spec.rb +83 -0
- data/spec/faraday/request_spec.rb +109 -0
- data/spec/faraday/response/logger_spec.rb +220 -0
- data/spec/faraday/response/middleware_spec.rb +68 -0
- data/spec/faraday/response/raise_error_spec.rb +106 -0
- data/spec/faraday/response_spec.rb +75 -0
- data/spec/faraday/utils/headers_spec.rb +82 -0
- data/spec/faraday/utils_spec.rb +56 -0
- data/spec/faraday_spec.rb +37 -0
- data/spec/spec_helper.rb +132 -0
- data/spec/support/disabling_stub.rb +14 -0
- data/spec/support/fake_safe_buffer.rb +15 -0
- data/spec/support/helper_methods.rb +133 -0
- data/spec/support/shared_examples/adapter.rb +104 -0
- data/spec/support/shared_examples/params_encoder.rb +18 -0
- data/spec/support/shared_examples/request_method.rb +234 -0
- data/spec/support/streaming_response_checker.rb +35 -0
- data/spec/support/webmock_rack_app.rb +68 -0
- metadata +126 -126
- data/Gemfile +0 -29
- data/config.ru +0 -6
- data/faraday.gemspec +0 -92
- data/lib/faraday/adapter/action_dispatch.rb +0 -29
- data/lib/faraday/builder.rb +0 -160
- data/lib/faraday/request/json.rb +0 -35
- data/lib/faraday/upload_io.rb +0 -23
- data/test/adapters/live_test.rb +0 -205
- data/test/adapters/logger_test.rb +0 -37
- data/test/adapters/net_http_test.rb +0 -33
- data/test/adapters/test_middleware_test.rb +0 -70
- data/test/connection_test.rb +0 -254
- data/test/env_test.rb +0 -158
- data/test/helper.rb +0 -41
- data/test/live_server.rb +0 -45
- data/test/middleware_stack_test.rb +0 -118
- data/test/request_middleware_test.rb +0 -116
- data/test/response_middleware_test.rb +0 -74
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Faraday
|
|
4
|
+
class Request
|
|
5
|
+
# Catches exceptions and retries each request a limited number of times.
|
|
6
|
+
#
|
|
7
|
+
# By default, it retries 2 times and handles only timeout exceptions. It can
|
|
8
|
+
# be configured with an arbitrary number of retries, a list of exceptions to
|
|
9
|
+
# handle, a retry interval, a percentage of randomness to add to the retry
|
|
10
|
+
# interval, and a backoff factor.
|
|
11
|
+
#
|
|
12
|
+
# @example Configure Retry middleware using intervals
|
|
13
|
+
# Faraday.new do |conn|
|
|
14
|
+
# conn.request(:retry, max: 2,
|
|
15
|
+
# interval: 0.05,
|
|
16
|
+
# interval_randomness: 0.5,
|
|
17
|
+
# backoff_factor: 2,
|
|
18
|
+
# exceptions: [CustomException, 'Timeout::Error'])
|
|
19
|
+
#
|
|
20
|
+
# conn.adapter(:net_http) # NB: Last middleware must be the adapter
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# This example will result in a first interval that is random between 0.05
|
|
24
|
+
# and 0.075 and a second interval that is random between 0.1 and 0.15.
|
|
25
|
+
class Retry < Faraday::Middleware
|
|
26
|
+
DEFAULT_EXCEPTIONS = [
|
|
27
|
+
Errno::ETIMEDOUT, 'Timeout::Error',
|
|
28
|
+
Faraday::TimeoutError, Faraday::RetriableResponse
|
|
29
|
+
].freeze
|
|
30
|
+
IDEMPOTENT_METHODS = %i[delete get head options put].freeze
|
|
31
|
+
|
|
32
|
+
# Options contains the configurable parameters for the Retry middleware.
|
|
33
|
+
class Options < Faraday::Options.new(:max, :interval, :max_interval,
|
|
34
|
+
:interval_randomness,
|
|
35
|
+
:backoff_factor, :exceptions,
|
|
36
|
+
:methods, :retry_if, :retry_block,
|
|
37
|
+
:retry_statuses)
|
|
38
|
+
|
|
39
|
+
DEFAULT_CHECK = ->(_env, _exception) { false }
|
|
40
|
+
|
|
41
|
+
def self.from(value)
|
|
42
|
+
if value.is_a?(Integer)
|
|
43
|
+
new(value)
|
|
44
|
+
else
|
|
45
|
+
super(value)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def max
|
|
50
|
+
(self[:max] ||= 2).to_i
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def interval
|
|
54
|
+
(self[:interval] ||= 0).to_f
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def max_interval
|
|
58
|
+
(self[:max_interval] ||= Float::MAX).to_f
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def interval_randomness
|
|
62
|
+
(self[:interval_randomness] ||= 0).to_f
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def backoff_factor
|
|
66
|
+
(self[:backoff_factor] ||= 1).to_f
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def exceptions
|
|
70
|
+
Array(self[:exceptions] ||= DEFAULT_EXCEPTIONS)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def methods
|
|
74
|
+
Array(self[:methods] ||= IDEMPOTENT_METHODS)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def retry_if
|
|
78
|
+
self[:retry_if] ||= DEFAULT_CHECK
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def retry_block
|
|
82
|
+
self[:retry_block] ||= proc {}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def retry_statuses
|
|
86
|
+
Array(self[:retry_statuses] ||= [])
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @param app [#call]
|
|
91
|
+
# @param options [Hash]
|
|
92
|
+
# @option options [Integer] :max (2) Maximum number of retries
|
|
93
|
+
# @option options [Integer] :interval (0) Pause in seconds between retries
|
|
94
|
+
# @option options [Integer] :interval_randomness (0) The maximum random
|
|
95
|
+
# interval amount expressed as a float between
|
|
96
|
+
# 0 and 1 to use in addition to the interval.
|
|
97
|
+
# @option options [Integer] :max_interval (Float::MAX) An upper limit
|
|
98
|
+
# for the interval
|
|
99
|
+
# @option options [Integer] :backoff_factor (1) The amount to multiply
|
|
100
|
+
# each successive retry's interval amount by in order to provide backoff
|
|
101
|
+
# @option options [Array] :exceptions ([ Errno::ETIMEDOUT,
|
|
102
|
+
# 'Timeout::Error', Faraday::TimeoutError, Faraday::RetriableResponse])
|
|
103
|
+
# The list of exceptions to handle. Exceptions can be given as
|
|
104
|
+
# Class, Module, or String.
|
|
105
|
+
# @option options [Array] :methods (the idempotent HTTP methods
|
|
106
|
+
# in IDEMPOTENT_METHODS) A list of HTTP methods to retry without
|
|
107
|
+
# calling retry_if. Pass an empty Array to call retry_if
|
|
108
|
+
# for all exceptions.
|
|
109
|
+
# @option options [Block] :retry_if (false) block that will receive
|
|
110
|
+
# the env object and the exception raised
|
|
111
|
+
# and should decide if the code should retry still the action or
|
|
112
|
+
# not independent of the retry count. This would be useful
|
|
113
|
+
# if the exception produced is non-recoverable or if the
|
|
114
|
+
# the HTTP method called is not idempotent.
|
|
115
|
+
# @option options [Block] :retry_block block that is executed after
|
|
116
|
+
# every retry. Request environment, middleware options, current number
|
|
117
|
+
# of retries and the exception is passed to the block as parameters.
|
|
118
|
+
# @option options [Array] :retry_statuses Array of Integer HTTP status
|
|
119
|
+
# codes or a single Integer value that determines whether to raise
|
|
120
|
+
# a Faraday::RetriableResponse exception based on the HTTP status code
|
|
121
|
+
# of an HTTP response.
|
|
122
|
+
def initialize(app, options = nil)
|
|
123
|
+
super(app)
|
|
124
|
+
@options = Options.from(options)
|
|
125
|
+
@errmatch = build_exception_matcher(@options.exceptions)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def calculate_sleep_amount(retries, env)
|
|
129
|
+
retry_after = calculate_retry_after(env)
|
|
130
|
+
retry_interval = calculate_retry_interval(retries)
|
|
131
|
+
|
|
132
|
+
return if retry_after && retry_after > @options.max_interval
|
|
133
|
+
|
|
134
|
+
if retry_after && retry_after >= retry_interval
|
|
135
|
+
retry_after
|
|
136
|
+
else
|
|
137
|
+
retry_interval
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# @param env [Faraday::Env]
|
|
142
|
+
def call(env)
|
|
143
|
+
retries = @options.max
|
|
144
|
+
request_body = env[:body]
|
|
145
|
+
begin
|
|
146
|
+
# after failure env[:body] is set to the response body
|
|
147
|
+
env[:body] = request_body
|
|
148
|
+
@app.call(env).tap do |resp|
|
|
149
|
+
if @options.retry_statuses.include?(resp.status)
|
|
150
|
+
raise Faraday::RetriableResponse.new(nil, resp)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
rescue @errmatch => e
|
|
154
|
+
if retries.positive? && retry_request?(env, e)
|
|
155
|
+
retries -= 1
|
|
156
|
+
rewind_files(request_body)
|
|
157
|
+
@options.retry_block.call(env, @options, retries, e)
|
|
158
|
+
if (sleep_amount = calculate_sleep_amount(retries + 1, env))
|
|
159
|
+
sleep sleep_amount
|
|
160
|
+
retry
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
raise unless e.is_a?(Faraday::RetriableResponse)
|
|
165
|
+
|
|
166
|
+
e.response
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# An exception matcher for the rescue clause can usually be any object
|
|
171
|
+
# that responds to `===`, but for Ruby 1.8 it has to be a Class or Module.
|
|
172
|
+
#
|
|
173
|
+
# @param exceptions [Array]
|
|
174
|
+
# @api private
|
|
175
|
+
# @return [Module] an exception matcher
|
|
176
|
+
def build_exception_matcher(exceptions)
|
|
177
|
+
matcher = Module.new
|
|
178
|
+
(
|
|
179
|
+
class << matcher
|
|
180
|
+
self
|
|
181
|
+
end).class_eval do
|
|
182
|
+
define_method(:===) do |error|
|
|
183
|
+
exceptions.any? do |ex|
|
|
184
|
+
if ex.is_a? Module
|
|
185
|
+
error.is_a? ex
|
|
186
|
+
else
|
|
187
|
+
error.class.to_s == ex.to_s
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
matcher
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
private
|
|
196
|
+
|
|
197
|
+
def retry_request?(env, exception)
|
|
198
|
+
@options.methods.include?(env[:method]) ||
|
|
199
|
+
@options.retry_if.call(env, exception)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def rewind_files(body)
|
|
203
|
+
return unless body.is_a?(Hash)
|
|
204
|
+
|
|
205
|
+
body.each do |_, value|
|
|
206
|
+
value.rewind if value.is_a?(UploadIO)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# MDN spec for Retry-After header:
|
|
211
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
|
|
212
|
+
def calculate_retry_after(env)
|
|
213
|
+
response_headers = env[:response_headers]
|
|
214
|
+
return unless response_headers
|
|
215
|
+
|
|
216
|
+
retry_after_value = env[:response_headers]['Retry-After']
|
|
217
|
+
|
|
218
|
+
# Try to parse date from the header value
|
|
219
|
+
begin
|
|
220
|
+
datetime = DateTime.rfc2822(retry_after_value)
|
|
221
|
+
datetime.to_time - Time.now.utc
|
|
222
|
+
rescue ArgumentError
|
|
223
|
+
retry_after_value.to_f
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def calculate_retry_interval(retries)
|
|
228
|
+
retry_index = @options.max - retries
|
|
229
|
+
current_interval = @options.interval *
|
|
230
|
+
(@options.backoff_factor**retry_index)
|
|
231
|
+
current_interval = [current_interval, @options.max_interval].min
|
|
232
|
+
random_interval = rand * @options.interval_randomness.to_f *
|
|
233
|
+
@options.interval
|
|
234
|
+
|
|
235
|
+
current_interval + random_interval
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Faraday
|
|
4
|
+
class Request
|
|
5
|
+
# TokenAuthentication is a middleware that adds a 'Token' header to a
|
|
6
|
+
# Faraday request.
|
|
7
|
+
class TokenAuthentication < load_middleware(:authorization)
|
|
8
|
+
# Public
|
|
9
|
+
def self.header(token, options = nil)
|
|
10
|
+
options ||= {}
|
|
11
|
+
options[:token] = token
|
|
12
|
+
super(:Token, options)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(app, token, options = nil)
|
|
16
|
+
super(app, token, options)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -1,37 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Faraday
|
|
2
|
-
class Request
|
|
3
|
-
|
|
4
|
+
class Request
|
|
5
|
+
# Middleware for supporting urlencoded requests.
|
|
6
|
+
class UrlEncoded < Faraday::Middleware
|
|
7
|
+
unless defined?(::Faraday::Request::UrlEncoded::CONTENT_TYPE)
|
|
8
|
+
CONTENT_TYPE = 'Content-Type'
|
|
9
|
+
end
|
|
4
10
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
class << self
|
|
12
|
+
attr_accessor :mime_type
|
|
13
|
+
end
|
|
14
|
+
self.mime_type = 'application/x-www-form-urlencoded'
|
|
9
15
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
# Encodes as "application/x-www-form-urlencoded" if not already encoded or
|
|
17
|
+
# of another type.
|
|
18
|
+
#
|
|
19
|
+
# @param env [Faraday::Env]
|
|
20
|
+
def call(env)
|
|
21
|
+
match_content_type(env) do |data|
|
|
22
|
+
params = Faraday::Utils::ParamsHash[data]
|
|
23
|
+
env.body = params.to_query(env.params_encoder)
|
|
24
|
+
end
|
|
25
|
+
@app.call env
|
|
13
26
|
end
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
env
|
|
22
|
-
yield env[:body] unless env[:body].respond_to?(:to_str)
|
|
27
|
+
|
|
28
|
+
# @param env [Faraday::Env]
|
|
29
|
+
# @yield [request_body] Body of the request
|
|
30
|
+
def match_content_type(env)
|
|
31
|
+
return unless process_request?(env)
|
|
32
|
+
|
|
33
|
+
env.request_headers[CONTENT_TYPE] ||= self.class.mime_type
|
|
34
|
+
yield(env.body) unless env.body.respond_to?(:to_str)
|
|
23
35
|
end
|
|
24
|
-
end
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
# @param env [Faraday::Env]
|
|
38
|
+
#
|
|
39
|
+
# @return [Boolean] True if the request has a body and its Content-Type is
|
|
40
|
+
# urlencoded.
|
|
41
|
+
def process_request?(env)
|
|
42
|
+
type = request_type(env)
|
|
43
|
+
env.body && (type.empty? || (type == self.class.mime_type))
|
|
44
|
+
end
|
|
30
45
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
46
|
+
# @param env [Faraday::Env]
|
|
47
|
+
#
|
|
48
|
+
# @return [String]
|
|
49
|
+
def request_type(env)
|
|
50
|
+
type = env.request_headers[CONTENT_TYPE].to_s
|
|
51
|
+
type = type.split(';', 2).first if type.index(';')
|
|
52
|
+
type
|
|
53
|
+
end
|
|
35
54
|
end
|
|
36
55
|
end
|
|
37
56
|
end
|
data/lib/faraday/request.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Faraday
|
|
2
|
-
# Used to setup
|
|
4
|
+
# Used to setup URLs, params, headers, and the request body in a sane manner.
|
|
3
5
|
#
|
|
6
|
+
# @example
|
|
4
7
|
# @connection.post do |req|
|
|
5
8
|
# req.url 'http://localhost', 'a' => '1' # 'http://localhost?a=1'
|
|
6
9
|
# req.headers['b'] = '2' # Header
|
|
@@ -9,76 +12,135 @@ module Faraday
|
|
|
9
12
|
# req.body = 'abc'
|
|
10
13
|
# end
|
|
11
14
|
#
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
# @!attribute method
|
|
16
|
+
# @return [Symbol] the HTTP method of the Request
|
|
17
|
+
# @!attribute path
|
|
18
|
+
# @return [URI, String] the path
|
|
19
|
+
# @!attribute params
|
|
20
|
+
# @return [Hash] query parameters
|
|
21
|
+
# @!attribute headers
|
|
22
|
+
# @return [Faraday::Utils::Headers] headers
|
|
23
|
+
# @!attribute body
|
|
24
|
+
# @return [Hash] body
|
|
25
|
+
# @!attribute options
|
|
26
|
+
# @return [RequestOptions] options
|
|
27
|
+
#
|
|
28
|
+
# rubocop:disable Style/StructInheritance
|
|
29
|
+
class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
|
|
30
|
+
# rubocop:enable Style/StructInheritance
|
|
19
31
|
|
|
20
|
-
|
|
21
|
-
:json => :JSON,
|
|
22
|
-
:url_encoded => :UrlEncoded,
|
|
23
|
-
:multipart => :Multipart
|
|
32
|
+
extend MiddlewareRegistry
|
|
24
33
|
|
|
25
|
-
|
|
34
|
+
register_middleware File.expand_path('request', __dir__),
|
|
35
|
+
url_encoded: [:UrlEncoded, 'url_encoded'],
|
|
36
|
+
multipart: [:Multipart, 'multipart'],
|
|
37
|
+
retry: [:Retry, 'retry'],
|
|
38
|
+
authorization: [:Authorization, 'authorization'],
|
|
39
|
+
basic_auth: [
|
|
40
|
+
:BasicAuthentication,
|
|
41
|
+
'basic_authentication'
|
|
42
|
+
],
|
|
43
|
+
token_auth: [
|
|
44
|
+
:TokenAuthentication,
|
|
45
|
+
'token_authentication'
|
|
46
|
+
],
|
|
47
|
+
instrumentation: [:Instrumentation, 'instrumentation']
|
|
26
48
|
|
|
49
|
+
# @param request_method [String]
|
|
50
|
+
# @yield [request] for block customization, if block given
|
|
51
|
+
# @yieldparam request [Request]
|
|
52
|
+
# @return [Request]
|
|
27
53
|
def self.create(request_method)
|
|
28
54
|
new(request_method).tap do |request|
|
|
29
|
-
yield
|
|
55
|
+
yield(request) if block_given?
|
|
30
56
|
end
|
|
31
57
|
end
|
|
32
58
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
59
|
+
# Replace params, preserving the existing hash type.
|
|
60
|
+
#
|
|
61
|
+
# @param hash [Hash] new params
|
|
62
|
+
def params=(hash)
|
|
63
|
+
if params
|
|
64
|
+
params.replace hash
|
|
65
|
+
else
|
|
66
|
+
super
|
|
67
|
+
end
|
|
38
68
|
end
|
|
39
69
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
70
|
+
# Replace request headers, preserving the existing hash type.
|
|
71
|
+
#
|
|
72
|
+
# @param hash [Hash] new headers
|
|
73
|
+
def headers=(hash)
|
|
74
|
+
if headers
|
|
75
|
+
headers.replace hash
|
|
76
|
+
else
|
|
77
|
+
super
|
|
78
|
+
end
|
|
43
79
|
end
|
|
44
80
|
|
|
81
|
+
# Update path and params.
|
|
82
|
+
#
|
|
83
|
+
# @param path [URI, String]
|
|
84
|
+
# @param params [Hash, nil]
|
|
85
|
+
# @return [void]
|
|
86
|
+
def url(path, params = nil)
|
|
87
|
+
if path.respond_to? :query
|
|
88
|
+
if (query = path.query)
|
|
89
|
+
path = path.dup
|
|
90
|
+
path.query = nil
|
|
91
|
+
end
|
|
92
|
+
else
|
|
93
|
+
anchor_index = path.index('#')
|
|
94
|
+
path = path.slice(0, anchor_index) unless anchor_index.nil?
|
|
95
|
+
path, query = path.split('?', 2)
|
|
96
|
+
end
|
|
97
|
+
self.path = path
|
|
98
|
+
self.params.merge_query query, options.params_encoder
|
|
99
|
+
self.params.update(params) if params
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @param key [Object] key to look up in headers
|
|
103
|
+
# @return [Object] value of the given header name
|
|
45
104
|
def [](key)
|
|
46
105
|
headers[key]
|
|
47
106
|
end
|
|
48
107
|
|
|
108
|
+
# @param key [Object] key of header to write
|
|
109
|
+
# @param value [Object] value of header
|
|
49
110
|
def []=(key, value)
|
|
50
111
|
headers[key] = value
|
|
51
112
|
end
|
|
52
113
|
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# :user - Proxy server username
|
|
67
|
-
# :password - Proxy server password
|
|
68
|
-
# :ssl - Hash of options for configuring SSL requests.
|
|
69
|
-
def to_env(connection)
|
|
70
|
-
env_params = connection.params.merge(params)
|
|
71
|
-
env_headers = connection.headers.merge(headers)
|
|
72
|
-
request_options = Utils.deep_merge(connection.options, options)
|
|
73
|
-
Utils.deep_merge!(request_options, :proxy => connection.proxy)
|
|
114
|
+
# Marshal serialization support.
|
|
115
|
+
#
|
|
116
|
+
# @return [Hash] the hash ready to be serialized in Marshal.
|
|
117
|
+
def marshal_dump
|
|
118
|
+
{
|
|
119
|
+
method: method,
|
|
120
|
+
body: body,
|
|
121
|
+
headers: headers,
|
|
122
|
+
path: path,
|
|
123
|
+
params: params,
|
|
124
|
+
options: options
|
|
125
|
+
}
|
|
126
|
+
end
|
|
74
127
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
128
|
+
# Marshal serialization support.
|
|
129
|
+
# Restores the instance variables according to the +serialised+.
|
|
130
|
+
# @param serialised [Hash] the serialised object.
|
|
131
|
+
def marshal_load(serialised)
|
|
132
|
+
self.method = serialised[:method]
|
|
133
|
+
self.body = serialised[:body]
|
|
134
|
+
self.headers = serialised[:headers]
|
|
135
|
+
self.path = serialised[:path]
|
|
136
|
+
self.params = serialised[:params]
|
|
137
|
+
self.options = serialised[:options]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @return [Env] the Env for this Request
|
|
141
|
+
def to_env(connection)
|
|
142
|
+
Env.new(method, body, connection.build_exclusive_url(path, params),
|
|
143
|
+
options, headers, connection.ssl, connection.parallel_manager)
|
|
82
144
|
end
|
|
83
145
|
end
|
|
84
146
|
end
|
|
@@ -1,34 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'forwardable'
|
|
4
|
+
require 'faraday/logging/formatter'
|
|
2
5
|
|
|
3
6
|
module Faraday
|
|
4
|
-
class Response
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
class Response
|
|
8
|
+
# Logger is a middleware that logs internal events in the HTTP request
|
|
9
|
+
# lifecycle to a given Logger object. By default, this logs to STDOUT. See
|
|
10
|
+
# Faraday::Logging::Formatter to see specifically what is logged.
|
|
11
|
+
class Logger < Middleware
|
|
12
|
+
def initialize(app, logger = nil, options = {})
|
|
13
|
+
super(app)
|
|
14
|
+
logger ||= begin
|
|
15
|
+
require 'logger'
|
|
16
|
+
::Logger.new($stdout)
|
|
17
|
+
end
|
|
18
|
+
formatter_class = options.delete(:formatter) || Logging::Formatter
|
|
19
|
+
@formatter = formatter_class.new(logger: logger, options: options)
|
|
20
|
+
yield @formatter if block_given?
|
|
12
21
|
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
|
16
|
-
|
|
17
|
-
def call(env)
|
|
18
|
-
info "#{env[:method]} #{env[:url].to_s}"
|
|
19
|
-
debug('request') { dump_headers env[:request_headers] }
|
|
20
|
-
super
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def on_complete(env)
|
|
24
|
-
info('Status') { env[:status].to_s }
|
|
25
|
-
debug('response') { dump_headers env[:response_headers] }
|
|
26
|
-
end
|
|
27
22
|
|
|
28
|
-
|
|
23
|
+
def call(env)
|
|
24
|
+
@formatter.request(env)
|
|
25
|
+
super
|
|
26
|
+
end
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
def on_complete(env)
|
|
29
|
+
@formatter.response(env)
|
|
30
|
+
end
|
|
32
31
|
end
|
|
33
32
|
end
|
|
34
33
|
end
|
|
@@ -1,16 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Faraday
|
|
2
|
-
class Response
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
class Response
|
|
5
|
+
# RaiseError is a Faraday middleware that raises exceptions on common HTTP
|
|
6
|
+
# client or server error responses.
|
|
7
|
+
class RaiseError < Middleware
|
|
8
|
+
# rubocop:disable Naming/ConstantName
|
|
9
|
+
ClientErrorStatuses = (400...500).freeze
|
|
10
|
+
ServerErrorStatuses = (500...600).freeze
|
|
11
|
+
# rubocop:enable Naming/ConstantName
|
|
12
|
+
|
|
13
|
+
def on_complete(env)
|
|
14
|
+
case env[:status]
|
|
15
|
+
when 400
|
|
16
|
+
raise Faraday::BadRequestError, response_values(env)
|
|
17
|
+
when 401
|
|
18
|
+
raise Faraday::UnauthorizedError, response_values(env)
|
|
19
|
+
when 403
|
|
20
|
+
raise Faraday::ForbiddenError, response_values(env)
|
|
21
|
+
when 404
|
|
22
|
+
raise Faraday::ResourceNotFound, response_values(env)
|
|
23
|
+
when 407
|
|
24
|
+
# mimic the behavior that we get with proxy requests with HTTPS
|
|
25
|
+
msg = %(407 "Proxy Authentication Required")
|
|
26
|
+
raise Faraday::ProxyAuthError.new(msg, response_values(env))
|
|
27
|
+
when 409
|
|
28
|
+
raise Faraday::ConflictError, response_values(env)
|
|
29
|
+
when 422
|
|
30
|
+
raise Faraday::UnprocessableEntityError, response_values(env)
|
|
31
|
+
when ClientErrorStatuses
|
|
32
|
+
raise Faraday::ClientError, response_values(env)
|
|
33
|
+
when ServerErrorStatuses
|
|
34
|
+
raise Faraday::ServerError, response_values(env)
|
|
35
|
+
when nil
|
|
36
|
+
raise Faraday::NilStatusError, response_values(env)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def response_values(env)
|
|
41
|
+
{ status: env.status, headers: env.response_headers, body: env.body }
|
|
9
42
|
end
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def response_values(env)
|
|
13
|
-
{:status => env[:status], :headers => env[:response_headers], :body => env[:body]}
|
|
14
43
|
end
|
|
15
44
|
end
|
|
16
45
|
end
|