faraday 1.1.0 → 2.9.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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -1
  3. data/LICENSE.md +1 -1
  4. data/README.md +34 -21
  5. data/Rakefile +3 -1
  6. data/examples/client_spec.rb +67 -13
  7. data/examples/client_test.rb +80 -15
  8. data/lib/faraday/adapter/test.rb +117 -52
  9. data/lib/faraday/adapter.rb +5 -20
  10. data/lib/faraday/connection.rb +70 -129
  11. data/lib/faraday/encoders/nested_params_encoder.rb +14 -7
  12. data/lib/faraday/error.rb +29 -8
  13. data/lib/faraday/logging/formatter.rb +28 -15
  14. data/lib/faraday/methods.rb +6 -0
  15. data/lib/faraday/middleware.rb +17 -5
  16. data/lib/faraday/middleware_registry.rb +17 -63
  17. data/lib/faraday/options/connection_options.rb +7 -6
  18. data/lib/faraday/options/env.rb +85 -62
  19. data/lib/faraday/options/proxy_options.rb +11 -3
  20. data/lib/faraday/options/request_options.rb +7 -6
  21. data/lib/faraday/options/ssl_options.rb +56 -45
  22. data/lib/faraday/options.rb +7 -6
  23. data/lib/faraday/rack_builder.rb +23 -21
  24. data/lib/faraday/request/authorization.rb +37 -38
  25. data/lib/faraday/request/instrumentation.rb +5 -1
  26. data/lib/faraday/request/json.rb +70 -0
  27. data/lib/faraday/request/url_encoded.rb +5 -1
  28. data/lib/faraday/request.rb +20 -37
  29. data/lib/faraday/response/json.rb +73 -0
  30. data/lib/faraday/response/logger.rb +8 -4
  31. data/lib/faraday/response/raise_error.rb +33 -6
  32. data/lib/faraday/response.rb +10 -26
  33. data/lib/faraday/utils/headers.rb +7 -2
  34. data/lib/faraday/utils.rb +11 -7
  35. data/lib/faraday/version.rb +5 -0
  36. data/lib/faraday.rb +49 -58
  37. data/spec/faraday/adapter/test_spec.rb +182 -0
  38. data/spec/faraday/connection_spec.rb +207 -90
  39. data/spec/faraday/error_spec.rb +45 -5
  40. data/spec/faraday/middleware_registry_spec.rb +31 -0
  41. data/spec/faraday/middleware_spec.rb +50 -6
  42. data/spec/faraday/options/env_spec.rb +8 -2
  43. data/spec/faraday/options/options_spec.rb +1 -1
  44. data/spec/faraday/options/proxy_options_spec.rb +15 -0
  45. data/spec/faraday/params_encoders/nested_spec.rb +8 -0
  46. data/spec/faraday/rack_builder_spec.rb +26 -54
  47. data/spec/faraday/request/authorization_spec.rb +54 -24
  48. data/spec/faraday/request/instrumentation_spec.rb +5 -7
  49. data/spec/faraday/request/json_spec.rb +199 -0
  50. data/spec/faraday/request/url_encoded_spec.rb +12 -2
  51. data/spec/faraday/request_spec.rb +5 -15
  52. data/spec/faraday/response/json_spec.rb +189 -0
  53. data/spec/faraday/response/logger_spec.rb +38 -0
  54. data/spec/faraday/response/raise_error_spec.rb +77 -5
  55. data/spec/faraday/response_spec.rb +3 -1
  56. data/spec/faraday/utils/headers_spec.rb +22 -4
  57. data/spec/faraday/utils_spec.rb +63 -1
  58. data/spec/faraday_spec.rb +8 -4
  59. data/spec/spec_helper.rb +6 -5
  60. data/spec/support/fake_safe_buffer.rb +1 -1
  61. data/spec/support/helper_methods.rb +0 -37
  62. data/spec/support/shared_examples/adapter.rb +4 -3
  63. data/spec/support/shared_examples/request_method.rb +58 -29
  64. metadata +17 -57
  65. data/lib/faraday/adapter/em_http.rb +0 -286
  66. data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -62
  67. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -69
  68. data/lib/faraday/adapter/em_synchrony.rb +0 -150
  69. data/lib/faraday/adapter/excon.rb +0 -124
  70. data/lib/faraday/adapter/httpclient.rb +0 -152
  71. data/lib/faraday/adapter/net_http.rb +0 -219
  72. data/lib/faraday/adapter/net_http_persistent.rb +0 -91
  73. data/lib/faraday/adapter/patron.rb +0 -132
  74. data/lib/faraday/adapter/rack.rb +0 -75
  75. data/lib/faraday/adapter/typhoeus.rb +0 -15
  76. data/lib/faraday/autoload.rb +0 -95
  77. data/lib/faraday/dependency_loader.rb +0 -39
  78. data/lib/faraday/file_part.rb +0 -128
  79. data/lib/faraday/param_part.rb +0 -53
  80. data/lib/faraday/request/basic_authentication.rb +0 -20
  81. data/lib/faraday/request/multipart.rb +0 -106
  82. data/lib/faraday/request/retry.rb +0 -239
  83. data/lib/faraday/request/token_authentication.rb +0 -20
  84. data/spec/faraday/adapter/em_http_spec.rb +0 -47
  85. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -16
  86. data/spec/faraday/adapter/excon_spec.rb +0 -49
  87. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  88. data/spec/faraday/adapter/net_http_persistent_spec.rb +0 -57
  89. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  90. data/spec/faraday/adapter/patron_spec.rb +0 -18
  91. data/spec/faraday/adapter/rack_spec.rb +0 -8
  92. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  93. data/spec/faraday/composite_read_io_spec.rb +0 -80
  94. data/spec/faraday/request/multipart_spec.rb +0 -302
  95. data/spec/faraday/request/retry_spec.rb +0 -242
  96. data/spec/faraday/response/middleware_spec.rb +0 -68
  97. data/spec/support/webmock_rack_app.rb +0 -68
@@ -4,52 +4,51 @@ module Faraday
4
4
  class Request
5
5
  # Request middleware for the Authorization HTTP header
6
6
  class Authorization < Faraday::Middleware
7
- unless defined?(::Faraday::Request::Authorization::KEY)
8
- KEY = 'Authorization'
9
- end
10
-
11
- # @param type [String, Symbol]
12
- # @param token [String, Symbol, Hash]
13
- # @return [String] a header value
14
- def self.header(type, token)
15
- case token
16
- when String, Symbol
17
- "#{type} #{token}"
18
- when Hash
19
- build_hash(type.to_s, token)
20
- else
21
- raise ArgumentError,
22
- "Can't build an Authorization #{type}" \
23
- "header from #{token.inspect}"
24
- end
25
- end
26
-
27
- # @param type [String]
28
- # @param hash [Hash]
29
- # @return [String] type followed by comma-separated key=value pairs
30
- # @api private
31
- def self.build_hash(type, hash)
32
- comma = ', '
33
- values = []
34
- hash.each do |key, value|
35
- values << "#{key}=#{value.to_s.inspect}"
36
- end
37
- "#{type} #{values * comma}"
38
- end
7
+ KEY = 'Authorization'
39
8
 
40
9
  # @param app [#call]
41
10
  # @param type [String, Symbol] Type of Authorization
42
- # @param token [String, Symbol, Hash] Token value for the Authorization
43
- def initialize(app, type, token)
44
- @header_value = self.class.header(type, token)
11
+ # @param params [Array<String, Proc, #call>] parameters to build the Authorization header.
12
+ # If the type is `:basic`, then these can be a login and password pair.
13
+ # Otherwise, a single value is expected that will be appended after the type.
14
+ # This value can be a proc or an object responding to `.call`, in which case
15
+ # it will be invoked on each request.
16
+ def initialize(app, type, *params)
17
+ @type = type
18
+ @params = params
45
19
  super(app)
46
20
  end
47
21
 
48
22
  # @param env [Faraday::Env]
49
- def call(env)
50
- env.request_headers[KEY] = @header_value unless env.request_headers[KEY]
51
- @app.call(env)
23
+ def on_request(env)
24
+ return if env.request_headers[KEY]
25
+
26
+ env.request_headers[KEY] = header_from(@type, env, *@params)
27
+ end
28
+
29
+ private
30
+
31
+ # @param type [String, Symbol]
32
+ # @param env [Faraday::Env]
33
+ # @param params [Array]
34
+ # @return [String] a header value
35
+ def header_from(type, env, *params)
36
+ if type.to_s.casecmp('basic').zero? && params.size == 2
37
+ Utils.basic_header_from(*params)
38
+ elsif params.size != 1
39
+ raise ArgumentError, "Unexpected params received (got #{params.size} instead of 1)"
40
+ else
41
+ value = params.first
42
+ if (value.is_a?(Proc) && value.arity == 1) || (value.respond_to?(:call) && value.method(:call).arity == 1)
43
+ value = value.call(env)
44
+ elsif value.is_a?(Proc) || value.respond_to?(:call)
45
+ value = value.call
46
+ end
47
+ "#{type} #{value}"
48
+ end
52
49
  end
53
50
  end
54
51
  end
55
52
  end
53
+
54
+ Faraday::Request.register_middleware(authorization: Faraday::Request::Authorization)
@@ -5,12 +5,14 @@ module Faraday
5
5
  # Middleware for instrumenting Requests.
6
6
  class Instrumentation < Faraday::Middleware
7
7
  # Options class used in Request::Instrumentation class.
8
- class Options < Faraday::Options.new(:name, :instrumenter)
8
+ Options = Faraday::Options.new(:name, :instrumenter) do
9
+ remove_method :name
9
10
  # @return [String]
10
11
  def name
11
12
  self[:name] ||= 'request.faraday'
12
13
  end
13
14
 
15
+ remove_method :instrumenter
14
16
  # @return [Class]
15
17
  def instrumenter
16
18
  self[:instrumenter] ||= ActiveSupport::Notifications
@@ -52,3 +54,5 @@ module Faraday
52
54
  end
53
55
  end
54
56
  end
57
+
58
+ Faraday::Request.register_middleware(instrumentation: Faraday::Request::Instrumentation)
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Faraday
6
+ class Request
7
+ # Request middleware that encodes the body as JSON.
8
+ #
9
+ # Processes only requests with matching Content-type or those without a type.
10
+ # If a request doesn't have a type but has a body, it sets the Content-type
11
+ # to JSON MIME-type.
12
+ #
13
+ # Doesn't try to encode bodies that already are in string form.
14
+ class Json < Middleware
15
+ MIME_TYPE = 'application/json'
16
+ MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}
17
+
18
+ def on_request(env)
19
+ match_content_type(env) do |data|
20
+ env[:body] = encode(data)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def encode(data)
27
+ if options[:encoder].is_a?(Array) && options[:encoder].size >= 2
28
+ options[:encoder][0].public_send(options[:encoder][1], data)
29
+ elsif options[:encoder].respond_to?(:dump)
30
+ options[:encoder].dump(data)
31
+ else
32
+ ::JSON.generate(data)
33
+ end
34
+ end
35
+
36
+ def match_content_type(env)
37
+ return unless process_request?(env)
38
+
39
+ env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE
40
+ yield env[:body] unless env[:body].respond_to?(:to_str)
41
+ end
42
+
43
+ def process_request?(env)
44
+ type = request_type(env)
45
+ body?(env) && (type.empty? || type.match?(MIME_TYPE_REGEX))
46
+ end
47
+
48
+ def body?(env)
49
+ body = env[:body]
50
+ case body
51
+ when true, false
52
+ true
53
+ when nil
54
+ # NOTE: nil can be converted to `"null"`, but this middleware doesn't process `nil` for the compatibility.
55
+ false
56
+ else
57
+ !(body.respond_to?(:to_str) && body.empty?)
58
+ end
59
+ end
60
+
61
+ def request_type(env)
62
+ type = env[:request_headers][CONTENT_TYPE].to_s
63
+ type = type.split(';', 2).first if type.index(';')
64
+ type
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ Faraday::Request.register_middleware(json: Faraday::Request::Json)
@@ -31,7 +31,9 @@ module Faraday
31
31
  return unless process_request?(env)
32
32
 
33
33
  env.request_headers[CONTENT_TYPE] ||= self.class.mime_type
34
- yield(env.body) unless env.body.respond_to?(:to_str)
34
+ return if env.body.respond_to?(:to_str) || env.body.respond_to?(:read)
35
+
36
+ yield(env.body)
35
37
  end
36
38
 
37
39
  # @param env [Faraday::Env]
@@ -54,3 +56,5 @@ module Faraday
54
56
  end
55
57
  end
56
58
  end
59
+
60
+ Faraday::Request.register_middleware(url_encoded: Faraday::Request::UrlEncoded)
@@ -21,32 +21,16 @@ module Faraday
21
21
  # @!attribute headers
22
22
  # @return [Faraday::Utils::Headers] headers
23
23
  # @!attribute body
24
- # @return [Hash] body
24
+ # @return [String] body
25
25
  # @!attribute options
26
26
  # @return [RequestOptions] options
27
- #
28
- # rubocop:disable Style/StructInheritance
29
- class Request < Struct.new(
30
- :http_method, :path, :params, :headers, :body, :options
31
- )
32
- # rubocop:enable Style/StructInheritance
33
-
27
+ Request = Struct.new(:http_method, :path, :params, :headers, :body, :options) do
34
28
  extend MiddlewareRegistry
35
29
 
36
- register_middleware File.expand_path('request', __dir__),
37
- url_encoded: [:UrlEncoded, 'url_encoded'],
38
- multipart: [:Multipart, 'multipart'],
39
- retry: [:Retry, 'retry'],
40
- authorization: [:Authorization, 'authorization'],
41
- basic_auth: [
42
- :BasicAuthentication,
43
- 'basic_authentication'
44
- ],
45
- token_auth: [
46
- :TokenAuthentication,
47
- 'token_authentication'
48
- ],
49
- instrumentation: [:Instrumentation, 'instrumentation']
30
+ alias_method :member_get, :[]
31
+ private :member_get
32
+ alias_method :member_set, :[]=
33
+ private :member_set
50
34
 
51
35
  # @param request_method [String]
52
36
  # @yield [request] for block customization, if block given
@@ -58,14 +42,7 @@ module Faraday
58
42
  end
59
43
  end
60
44
 
61
- def method
62
- warn <<~TEXT
63
- WARNING: `Faraday::Request##{__method__}` is deprecated; use `#http_method` instead. It will be removed in or after version 2.0.
64
- `Faraday::Request##{__method__}` called from #{caller_locations(1..1).first}
65
- TEXT
66
- http_method
67
- end
68
-
45
+ remove_method :params=
69
46
  # Replace params, preserving the existing hash type.
70
47
  #
71
48
  # @param hash [Hash] new params
@@ -73,10 +50,11 @@ module Faraday
73
50
  if params
74
51
  params.replace hash
75
52
  else
76
- super
53
+ member_set(:params, hash)
77
54
  end
78
55
  end
79
56
 
57
+ remove_method :headers=
80
58
  # Replace request headers, preserving the existing hash type.
81
59
  #
82
60
  # @param hash [Hash] new headers
@@ -84,7 +62,7 @@ module Faraday
84
62
  if headers
85
63
  headers.replace hash
86
64
  else
87
- super
65
+ member_set(:headers, hash)
88
66
  end
89
67
  end
90
68
 
@@ -140,11 +118,11 @@ module Faraday
140
118
  # @param serialised [Hash] the serialised object.
141
119
  def marshal_load(serialised)
142
120
  self.http_method = serialised[:http_method]
143
- self.body = serialised[:body]
144
- self.headers = serialised[:headers]
145
- self.path = serialised[:path]
146
- self.params = serialised[:params]
147
- self.options = serialised[:options]
121
+ self.body = serialised[:body]
122
+ self.headers = serialised[:headers]
123
+ self.path = serialised[:path]
124
+ self.params = serialised[:params]
125
+ self.options = serialised[:options]
148
126
  end
149
127
 
150
128
  # @return [Env] the Env for this Request
@@ -154,3 +132,8 @@ module Faraday
154
132
  end
155
133
  end
156
134
  end
135
+
136
+ require 'faraday/request/authorization'
137
+ require 'faraday/request/instrumentation'
138
+ require 'faraday/request/json'
139
+ require 'faraday/request/url_encoded'
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Faraday
6
+ class Response
7
+ # Parse response bodies as JSON.
8
+ class Json < Middleware
9
+ def initialize(app = nil, parser_options: nil, content_type: /\bjson$/, preserve_raw: false)
10
+ super(app)
11
+ @parser_options = parser_options
12
+ @content_types = Array(content_type)
13
+ @preserve_raw = preserve_raw
14
+
15
+ process_parser_options
16
+ end
17
+
18
+ def on_complete(env)
19
+ process_response(env) if parse_response?(env)
20
+ end
21
+
22
+ private
23
+
24
+ def process_response(env)
25
+ env[:raw_body] = env[:body] if @preserve_raw
26
+ env[:body] = parse(env[:body])
27
+ rescue StandardError, SyntaxError => e
28
+ raise Faraday::ParsingError.new(e, env[:response])
29
+ end
30
+
31
+ def parse(body)
32
+ return if body.strip.empty?
33
+
34
+ decoder, method_name = @decoder_options
35
+
36
+ decoder.public_send(method_name, body, @parser_options || {})
37
+ end
38
+
39
+ def parse_response?(env)
40
+ process_response_type?(env) &&
41
+ env[:body].respond_to?(:to_str)
42
+ end
43
+
44
+ def process_response_type?(env)
45
+ type = response_type(env)
46
+ @content_types.empty? || @content_types.any? do |pattern|
47
+ pattern.is_a?(Regexp) ? type.match?(pattern) : type == pattern
48
+ end
49
+ end
50
+
51
+ def response_type(env)
52
+ type = env[:response_headers][CONTENT_TYPE].to_s
53
+ type = type.split(';', 2).first if type.index(';')
54
+ type
55
+ end
56
+
57
+ def process_parser_options
58
+ @decoder_options = @parser_options&.delete(:decoder)
59
+
60
+ @decoder_options =
61
+ if @decoder_options.is_a?(Array) && @decoder_options.size >= 2
62
+ @decoder_options.slice(0, 2)
63
+ elsif @decoder_options.respond_to?(:load)
64
+ [@decoder_options, :load]
65
+ else
66
+ [::JSON, :parse]
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ Faraday::Response.register_middleware(json: Faraday::Response::Json)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
+ require 'logger'
4
5
  require 'faraday/logging/formatter'
5
6
 
6
7
  module Faraday
@@ -11,10 +12,7 @@ module Faraday
11
12
  class Logger < Middleware
12
13
  def initialize(app, logger = nil, options = {})
13
14
  super(app)
14
- logger ||= begin
15
- require 'logger'
16
- ::Logger.new($stdout)
17
- end
15
+ logger ||= ::Logger.new($stdout)
18
16
  formatter_class = options.delete(:formatter) || Logging::Formatter
19
17
  @formatter = formatter_class.new(logger: logger, options: options)
20
18
  yield @formatter if block_given?
@@ -28,6 +26,12 @@ module Faraday
28
26
  def on_complete(env)
29
27
  @formatter.response(env)
30
28
  end
29
+
30
+ def on_error(exc)
31
+ @formatter.exception(exc) if @formatter.respond_to?(:exception)
32
+ end
31
33
  end
32
34
  end
33
35
  end
36
+
37
+ Faraday::Response.register_middleware(logger: Faraday::Response::Logger)
@@ -6,8 +6,8 @@ module Faraday
6
6
  # client or server error responses.
7
7
  class RaiseError < Middleware
8
8
  # rubocop:disable Naming/ConstantName
9
- ClientErrorStatuses = (400...500).freeze
10
- ServerErrorStatuses = (500...600).freeze
9
+ ClientErrorStatuses = (400...500)
10
+ ServerErrorStatuses = (500...600)
11
11
  # rubocop:enable Naming/ConstantName
12
12
 
13
13
  def on_complete(env)
@@ -24,10 +24,14 @@ module Faraday
24
24
  # mimic the behavior that we get with proxy requests with HTTPS
25
25
  msg = %(407 "Proxy Authentication Required")
26
26
  raise Faraday::ProxyAuthError.new(msg, response_values(env))
27
+ when 408
28
+ raise Faraday::RequestTimeoutError, response_values(env)
27
29
  when 409
28
30
  raise Faraday::ConflictError, response_values(env)
29
31
  when 422
30
32
  raise Faraday::UnprocessableEntityError, response_values(env)
33
+ when 429
34
+ raise Faraday::TooManyRequestsError, response_values(env)
31
35
  when ClientErrorStatuses
32
36
  raise Faraday::ClientError, response_values(env)
33
37
  when ServerErrorStatuses
@@ -37,20 +41,43 @@ module Faraday
37
41
  end
38
42
  end
39
43
 
44
+ # Returns a hash of response data with the following keys:
45
+ # - status
46
+ # - headers
47
+ # - body
48
+ # - request
49
+ #
50
+ # The `request` key is omitted when the middleware is explicitly
51
+ # configured with the option `include_request: false`.
40
52
  def response_values(env)
41
- {
53
+ response = {
42
54
  status: env.status,
43
55
  headers: env.response_headers,
44
- body: env.body,
56
+ body: env.body
57
+ }
58
+
59
+ # Include the request data by default. If the middleware was explicitly
60
+ # configured to _not_ include request data, then omit it.
61
+ return response unless options.fetch(:include_request, true)
62
+
63
+ response.merge(
45
64
  request: {
46
65
  method: env.method,
66
+ url: env.url,
47
67
  url_path: env.url.path,
48
- params: env.params,
68
+ params: query_params(env),
49
69
  headers: env.request_headers,
50
70
  body: env.request_body
51
71
  }
52
- }
72
+ )
73
+ end
74
+
75
+ def query_params(env)
76
+ env.request.params_encoder ||= Faraday::Utils.default_params_encoder
77
+ env.params_encoder.decode(env.url.query)
53
78
  end
54
79
  end
55
80
  end
56
81
  end
82
+
83
+ Faraday::Response.register_middleware(raise_error: Faraday::Response::RaiseError)
@@ -5,31 +5,9 @@ require 'forwardable'
5
5
  module Faraday
6
6
  # Response represents an HTTP response from making an HTTP request.
7
7
  class Response
8
- # Used for simple response middleware.
9
- class Middleware < Faraday::Middleware
10
- def call(env)
11
- @app.call(env).on_complete do |environment|
12
- on_complete(environment)
13
- end
14
- end
15
-
16
- # Override this to modify the environment after the response has finished.
17
- # Calls the `parse` method if defined
18
- # `parse` method can be defined as private, public and protected
19
- def on_complete(env)
20
- return unless respond_to?(:parse, true) && env.parse_body?
21
-
22
- env.body = parse(env.body)
23
- end
24
- end
25
-
26
8
  extend Forwardable
27
9
  extend MiddlewareRegistry
28
10
 
29
- register_middleware File.expand_path('response', __dir__),
30
- raise_error: [:RaiseError, 'raise_error'],
31
- logger: [:Logger, 'logger']
32
-
33
11
  def initialize(env = nil)
34
12
  @env = Env.from(env) if env
35
13
  @on_complete_callbacks = []
@@ -48,6 +26,7 @@ module Faraday
48
26
  def headers
49
27
  finished? ? env.response_headers : {}
50
28
  end
29
+
51
30
  def_delegator :headers, :[]
52
31
 
53
32
  def body
@@ -59,10 +38,10 @@ module Faraday
59
38
  end
60
39
 
61
40
  def on_complete(&block)
62
- if !finished?
63
- @on_complete_callbacks << block
64
- else
41
+ if finished?
65
42
  yield(env)
43
+ else
44
+ @on_complete_callbacks << block
66
45
  end
67
46
  self
68
47
  end
@@ -82,7 +61,8 @@ module Faraday
82
61
  def to_hash
83
62
  {
84
63
  status: env.status, body: env.body,
85
- response_headers: env.response_headers
64
+ response_headers: env.response_headers,
65
+ url: env.url
86
66
  }
87
67
  end
88
68
 
@@ -105,3 +85,7 @@ module Faraday
105
85
  end
106
86
  end
107
87
  end
88
+
89
+ require 'faraday/response/json'
90
+ require 'faraday/response/logger'
91
+ require 'faraday/response/raise_error'
@@ -111,7 +111,7 @@ module Faraday
111
111
  def parse(header_string)
112
112
  return unless header_string && !header_string.empty?
113
113
 
114
- headers = header_string.split(/\r\n/)
114
+ headers = header_string.split("\r\n")
115
115
 
116
116
  # Find the last set of response headers.
117
117
  start_index = headers.rindex { |x| x.start_with?('HTTP/') } || 0
@@ -132,7 +132,12 @@ module Faraday
132
132
 
133
133
  # Join multiple values with a comma.
134
134
  def add_parsed(key, value)
135
- self[key] ? self[key] << ', ' << value : self[key] = value
135
+ if key?(key)
136
+ self[key] = self[key].to_s
137
+ self[key] << ', ' << value
138
+ else
139
+ self[key] = value
140
+ end
136
141
  end
137
142
  end
138
143
  end
data/lib/faraday/utils.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'uri'
3
4
  require 'faraday/utils/headers'
4
5
  require 'faraday/utils/params_hash'
5
6
 
@@ -24,7 +25,7 @@ module Faraday
24
25
  attr_writer :default_space_encoding
25
26
  end
26
27
 
27
- ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/.freeze
28
+ ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
28
29
 
29
30
  def escape(str)
30
31
  str.to_s.gsub(ESCAPE_RE) do |match|
@@ -36,7 +37,7 @@ module Faraday
36
37
  CGI.unescape str.to_s
37
38
  end
38
39
 
39
- DEFAULT_SEP = /[&;] */n.freeze
40
+ DEFAULT_SEP = /[&;] */n
40
41
 
41
42
  # Adapted from Rack
42
43
  def parse_query(query)
@@ -51,6 +52,12 @@ module Faraday
51
52
  @default_params_encoder ||= NestedParamsEncoder
52
53
  end
53
54
 
55
+ def basic_header_from(login, pass)
56
+ value = ["#{login}:#{pass}"].pack('m') # Base64 encoding
57
+ value.delete!("\n")
58
+ "Basic #{value}"
59
+ end
60
+
54
61
  class << self
55
62
  attr_writer :default_params_encoder
56
63
  end
@@ -71,10 +78,7 @@ module Faraday
71
78
  end
72
79
 
73
80
  def default_uri_parser
74
- @default_uri_parser ||= begin
75
- require 'uri'
76
- Kernel.method(:URI)
77
- end
81
+ @default_uri_parser ||= Kernel.method(:URI)
78
82
  end
79
83
 
80
84
  def default_uri_parser=(parser)
@@ -96,7 +100,7 @@ module Faraday
96
100
  # Recursive hash update
97
101
  def deep_merge!(target, hash)
98
102
  hash.each do |key, value|
99
- target[key] = if value.is_a?(Hash) && target[key].is_a?(Hash)
103
+ target[key] = if value.is_a?(Hash) && (target[key].is_a?(Hash) || target[key].is_a?(Options))
100
104
  deep_merge(target[key], value)
101
105
  else
102
106
  value
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ VERSION = '2.9.0'
5
+ end