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.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +276 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +40 -153
  5. data/Rakefile +4 -139
  6. data/examples/client_spec.rb +65 -0
  7. data/examples/client_test.rb +79 -0
  8. data/lib/faraday/adapter/em_http.rb +286 -0
  9. data/lib/faraday/adapter/em_http_ssl_patch.rb +62 -0
  10. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +69 -0
  11. data/lib/faraday/adapter/em_synchrony.rb +120 -36
  12. data/lib/faraday/adapter/excon.rb +108 -12
  13. data/lib/faraday/adapter/httpclient.rb +152 -0
  14. data/lib/faraday/adapter/net_http.rb +187 -43
  15. data/lib/faraday/adapter/net_http_persistent.rb +91 -0
  16. data/lib/faraday/adapter/patron.rb +106 -10
  17. data/lib/faraday/adapter/rack.rb +75 -0
  18. data/lib/faraday/adapter/test.rb +160 -61
  19. data/lib/faraday/adapter/typhoeus.rb +7 -46
  20. data/lib/faraday/adapter.rb +105 -33
  21. data/lib/faraday/adapter_registry.rb +30 -0
  22. data/lib/faraday/autoload.rb +95 -0
  23. data/lib/faraday/connection.rb +525 -157
  24. data/lib/faraday/dependency_loader.rb +37 -0
  25. data/lib/faraday/encoders/flat_params_encoder.rb +98 -0
  26. data/lib/faraday/encoders/nested_params_encoder.rb +171 -0
  27. data/lib/faraday/error.rb +122 -30
  28. data/lib/faraday/file_part.rb +128 -0
  29. data/lib/faraday/logging/formatter.rb +105 -0
  30. data/lib/faraday/middleware.rb +14 -22
  31. data/lib/faraday/middleware_registry.rb +129 -0
  32. data/lib/faraday/options/connection_options.rb +22 -0
  33. data/lib/faraday/options/env.rb +181 -0
  34. data/lib/faraday/options/proxy_options.rb +28 -0
  35. data/lib/faraday/options/request_options.rb +22 -0
  36. data/lib/faraday/options/ssl_options.rb +59 -0
  37. data/lib/faraday/options.rb +222 -0
  38. data/lib/faraday/param_part.rb +53 -0
  39. data/lib/faraday/parameters.rb +5 -0
  40. data/lib/faraday/rack_builder.rb +248 -0
  41. data/lib/faraday/request/authorization.rb +55 -0
  42. data/lib/faraday/request/basic_authentication.rb +20 -0
  43. data/lib/faraday/request/instrumentation.rb +54 -0
  44. data/lib/faraday/request/multipart.rb +84 -48
  45. data/lib/faraday/request/retry.rb +239 -0
  46. data/lib/faraday/request/token_authentication.rb +20 -0
  47. data/lib/faraday/request/url_encoded.rb +46 -27
  48. data/lib/faraday/request.rb +112 -50
  49. data/lib/faraday/response/logger.rb +24 -25
  50. data/lib/faraday/response/raise_error.rb +40 -11
  51. data/lib/faraday/response.rb +44 -35
  52. data/lib/faraday/utils/headers.rb +139 -0
  53. data/lib/faraday/utils/params_hash.rb +61 -0
  54. data/lib/faraday/utils.rb +72 -117
  55. data/lib/faraday.rb +142 -64
  56. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  57. data/spec/faraday/adapter/em_http_spec.rb +47 -0
  58. data/spec/faraday/adapter/em_synchrony_spec.rb +16 -0
  59. data/spec/faraday/adapter/excon_spec.rb +49 -0
  60. data/spec/faraday/adapter/httpclient_spec.rb +73 -0
  61. data/spec/faraday/adapter/net_http_persistent_spec.rb +57 -0
  62. data/spec/faraday/adapter/net_http_spec.rb +64 -0
  63. data/spec/faraday/adapter/patron_spec.rb +18 -0
  64. data/spec/faraday/adapter/rack_spec.rb +8 -0
  65. data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
  66. data/spec/faraday/adapter_registry_spec.rb +28 -0
  67. data/spec/faraday/adapter_spec.rb +55 -0
  68. data/spec/faraday/composite_read_io_spec.rb +80 -0
  69. data/spec/faraday/connection_spec.rb +691 -0
  70. data/spec/faraday/error_spec.rb +45 -0
  71. data/spec/faraday/middleware_spec.rb +26 -0
  72. data/spec/faraday/options/env_spec.rb +70 -0
  73. data/spec/faraday/options/options_spec.rb +297 -0
  74. data/spec/faraday/options/proxy_options_spec.rb +37 -0
  75. data/spec/faraday/options/request_options_spec.rb +19 -0
  76. data/spec/faraday/params_encoders/flat_spec.rb +34 -0
  77. data/spec/faraday/params_encoders/nested_spec.rb +134 -0
  78. data/spec/faraday/rack_builder_spec.rb +196 -0
  79. data/spec/faraday/request/authorization_spec.rb +88 -0
  80. data/spec/faraday/request/instrumentation_spec.rb +76 -0
  81. data/spec/faraday/request/multipart_spec.rb +274 -0
  82. data/spec/faraday/request/retry_spec.rb +242 -0
  83. data/spec/faraday/request/url_encoded_spec.rb +83 -0
  84. data/spec/faraday/request_spec.rb +109 -0
  85. data/spec/faraday/response/logger_spec.rb +220 -0
  86. data/spec/faraday/response/middleware_spec.rb +68 -0
  87. data/spec/faraday/response/raise_error_spec.rb +106 -0
  88. data/spec/faraday/response_spec.rb +75 -0
  89. data/spec/faraday/utils/headers_spec.rb +82 -0
  90. data/spec/faraday/utils_spec.rb +56 -0
  91. data/spec/faraday_spec.rb +37 -0
  92. data/spec/spec_helper.rb +132 -0
  93. data/spec/support/disabling_stub.rb +14 -0
  94. data/spec/support/fake_safe_buffer.rb +15 -0
  95. data/spec/support/helper_methods.rb +133 -0
  96. data/spec/support/shared_examples/adapter.rb +104 -0
  97. data/spec/support/shared_examples/params_encoder.rb +18 -0
  98. data/spec/support/shared_examples/request_method.rb +234 -0
  99. data/spec/support/streaming_response_checker.rb +35 -0
  100. data/spec/support/webmock_rack_app.rb +68 -0
  101. metadata +126 -126
  102. data/Gemfile +0 -29
  103. data/config.ru +0 -6
  104. data/faraday.gemspec +0 -92
  105. data/lib/faraday/adapter/action_dispatch.rb +0 -29
  106. data/lib/faraday/builder.rb +0 -160
  107. data/lib/faraday/request/json.rb +0 -35
  108. data/lib/faraday/upload_io.rb +0 -23
  109. data/test/adapters/live_test.rb +0 -205
  110. data/test/adapters/logger_test.rb +0 -37
  111. data/test/adapters/net_http_test.rb +0 -33
  112. data/test/adapters/test_middleware_test.rb +0 -70
  113. data/test/connection_test.rb +0 -254
  114. data/test/env_test.rb +0 -158
  115. data/test/helper.rb +0 -41
  116. data/test/live_server.rb +0 -45
  117. data/test/middleware_stack_test.rb +0 -118
  118. data/test/request_middleware_test.rb +0 -116
  119. 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::UrlEncoded < Faraday::Middleware
3
- CONTENT_TYPE = 'Content-Type'.freeze
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
- class << self
6
- attr_accessor :mime_type
7
- end
8
- self.mime_type = 'application/x-www-form-urlencoded'.freeze
11
+ class << self
12
+ attr_accessor :mime_type
13
+ end
14
+ self.mime_type = 'application/x-www-form-urlencoded'
9
15
 
10
- def call(env)
11
- match_content_type(env) do |data|
12
- env[:body] = Faraday::Utils.build_nested_query data
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
- @app.call env
15
- end
16
-
17
- def match_content_type(env)
18
- type = request_type(env)
19
-
20
- if process_request?(env)
21
- env[:request_headers][CONTENT_TYPE] ||= self.class.mime_type
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
- def process_request?(env)
27
- type = request_type(env)
28
- env[:body] and (type.empty? or type == self.class.mime_type)
29
- end
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
- def request_type(env)
32
- type = env[:request_headers][CONTENT_TYPE].to_s
33
- type = type.split(';', 2).first if type.index(';')
34
- type
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
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
- # Used to setup urls, params, headers, and the request body in a sane manner.
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
- class Request < Struct.new(:path, :params, :headers, :body, :options)
13
- extend AutoloadHelper
14
-
15
- autoload_all 'faraday/request',
16
- :JSON => 'json',
17
- :UrlEncoded => 'url_encoded',
18
- :Multipart => 'multipart'
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
- register_lookup_modules \
21
- :json => :JSON,
22
- :url_encoded => :UrlEncoded,
23
- :multipart => :Multipart
32
+ extend MiddlewareRegistry
24
33
 
25
- attr_reader :method
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 request if block_given?
55
+ yield(request) if block_given?
30
56
  end
31
57
  end
32
58
 
33
- def initialize(request_method)
34
- @method = request_method
35
- self.params = {}
36
- self.headers = {}
37
- self.options = {}
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
- def url(path, params = {})
41
- self.path = path
42
- self.params = params
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
- # ENV Keys
54
- # :method - a symbolized request method (:get, :post)
55
- # :body - the request body that will eventually be converted to a string.
56
- # :url - Addressable::URI instance of the URI for the current request.
57
- # :status - HTTP response status code
58
- # :request_headers - hash of HTTP Headers to be sent to the server
59
- # :response_headers - Hash of HTTP headers from the server
60
- # :parallel_manager - sent if the connection is in parallel mode
61
- # :request - Hash of options for configuring the request.
62
- # :timeout - open/read timeout Integer in seconds
63
- # :open_timeout - read timeout Integer in seconds
64
- # :proxy - Hash of proxy options
65
- # :uri - Proxy Server URI
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
- { :method => method,
76
- :body => body,
77
- :url => connection.build_url(path, env_params),
78
- :request_headers => env_headers,
79
- :parallel_manager => connection.parallel_manager,
80
- :request => request_options,
81
- :ssl => connection.ssl}
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::Logger < Response::Middleware
5
- extend Forwardable
6
-
7
- def initialize(app, logger = nil)
8
- super(app)
9
- @logger = logger || begin
10
- require 'logger'
11
- ::Logger.new(STDOUT)
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
- private
23
+ def call(env)
24
+ @formatter.request(env)
25
+ super
26
+ end
29
27
 
30
- def dump_headers(headers)
31
- headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
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::RaiseError < Response::Middleware
3
- def on_complete(env)
4
- case env[:status]
5
- when 404
6
- raise Faraday::Error::ResourceNotFound, response_values(env)
7
- when 400...600
8
- raise Faraday::Error::ClientError, response_values(env)
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