faraday 1.10.4 → 2.14.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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +198 -4
  3. data/LICENSE.md +1 -1
  4. data/README.md +34 -20
  5. data/Rakefile +6 -1
  6. data/examples/client_spec.rb +41 -19
  7. data/examples/client_test.rb +48 -22
  8. data/lib/faraday/adapter/test.rb +62 -13
  9. data/lib/faraday/adapter.rb +6 -10
  10. data/lib/faraday/connection.rb +72 -150
  11. data/lib/faraday/encoders/flat_params_encoder.rb +2 -2
  12. data/lib/faraday/encoders/nested_params_encoder.rb +14 -7
  13. data/lib/faraday/error.rb +66 -10
  14. data/lib/faraday/logging/formatter.rb +30 -17
  15. data/lib/faraday/middleware.rb +43 -2
  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 -5
  20. data/lib/faraday/options/request_options.rb +7 -6
  21. data/lib/faraday/options/ssl_options.rb +62 -45
  22. data/lib/faraday/options.rb +7 -6
  23. data/lib/faraday/rack_builder.rb +44 -45
  24. data/lib/faraday/request/authorization.rb +33 -41
  25. data/lib/faraday/request/instrumentation.rb +5 -1
  26. data/lib/faraday/request/json.rb +18 -3
  27. data/lib/faraday/request/url_encoded.rb +5 -1
  28. data/lib/faraday/request.rb +15 -30
  29. data/lib/faraday/response/json.rb +25 -5
  30. data/lib/faraday/response/logger.rb +11 -3
  31. data/lib/faraday/response/raise_error.rb +45 -18
  32. data/lib/faraday/response.rb +14 -22
  33. data/lib/faraday/utils/headers.rb +15 -4
  34. data/lib/faraday/utils.rb +11 -7
  35. data/lib/faraday/version.rb +1 -1
  36. data/lib/faraday.rb +10 -45
  37. data/spec/faraday/adapter/test_spec.rb +65 -0
  38. data/spec/faraday/connection_spec.rb +165 -93
  39. data/spec/faraday/error_spec.rb +122 -7
  40. data/spec/faraday/middleware_registry_spec.rb +31 -0
  41. data/spec/faraday/middleware_spec.rb +161 -0
  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 +35 -0
  45. data/spec/faraday/params_encoders/nested_spec.rb +10 -1
  46. data/spec/faraday/rack_builder_spec.rb +26 -54
  47. data/spec/faraday/request/authorization_spec.rb +50 -28
  48. data/spec/faraday/request/instrumentation_spec.rb +5 -7
  49. data/spec/faraday/request/json_spec.rb +88 -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 +93 -6
  53. data/spec/faraday/response/logger_spec.rb +83 -4
  54. data/spec/faraday/response/raise_error_spec.rb +133 -16
  55. data/spec/faraday/response_spec.rb +10 -1
  56. data/spec/faraday/utils/headers_spec.rb +31 -4
  57. data/spec/faraday/utils_spec.rb +65 -1
  58. data/spec/faraday_spec.rb +10 -4
  59. data/spec/spec_helper.rb +5 -6
  60. data/spec/support/fake_safe_buffer.rb +1 -1
  61. data/spec/support/faraday_middleware_subclasses.rb +18 -0
  62. data/spec/support/helper_methods.rb +0 -37
  63. data/spec/support/shared_examples/adapter.rb +2 -2
  64. data/spec/support/shared_examples/request_method.rb +22 -21
  65. metadata +24 -149
  66. data/lib/faraday/adapter/typhoeus.rb +0 -15
  67. data/lib/faraday/autoload.rb +0 -89
  68. data/lib/faraday/dependency_loader.rb +0 -39
  69. data/lib/faraday/deprecate.rb +0 -110
  70. data/lib/faraday/request/basic_authentication.rb +0 -20
  71. data/lib/faraday/request/token_authentication.rb +0 -20
  72. data/spec/faraday/adapter/em_http_spec.rb +0 -49
  73. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -18
  74. data/spec/faraday/adapter/excon_spec.rb +0 -49
  75. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  76. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  77. data/spec/faraday/adapter/patron_spec.rb +0 -18
  78. data/spec/faraday/adapter/rack_spec.rb +0 -8
  79. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  80. data/spec/faraday/composite_read_io_spec.rb +0 -80
  81. data/spec/faraday/deprecate_spec.rb +0 -147
  82. data/spec/faraday/response/middleware_spec.rb +0 -68
  83. data/spec/support/webmock_rack_app.rb +0 -68
@@ -1,53 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'base64'
4
-
5
3
  module Faraday
6
4
  class Request
7
5
  # Request middleware for the Authorization HTTP header
8
6
  class Authorization < Faraday::Middleware
9
- unless defined?(::Faraday::Request::Authorization::KEY)
10
- KEY = 'Authorization'
11
- end
12
-
13
- # @param type [String, Symbol]
14
- # @param token [String, Symbol, Hash]
15
- # @return [String] a header value
16
- def self.header(type, token)
17
- case token
18
- when String, Symbol, Proc
19
- token = token.call if token.is_a?(Proc)
20
- "#{type} #{token}"
21
- when Hash
22
- build_hash(type.to_s, token)
23
- else
24
- raise ArgumentError,
25
- "Can't build an Authorization #{type}" \
26
- "header from #{token.inspect}"
27
- end
28
- end
29
-
30
- # @param type [String]
31
- # @param hash [Hash]
32
- # @return [String] type followed by comma-separated key=value pairs
33
- # @api private
34
- def self.build_hash(type, hash)
35
- comma = ', '
36
- values = []
37
- hash.each do |key, value|
38
- value = value.call if value.is_a?(Proc)
39
- values << "#{key}=#{value.to_s.inspect}"
40
- end
41
- "#{type} #{values * comma}"
42
- end
7
+ KEY = 'Authorization'
43
8
 
44
9
  # @param app [#call]
45
10
  # @param type [String, Symbol] Type of Authorization
46
- # @param param [String, Symbol, Hash, Proc] parameter to build the Authorization header.
47
- # This value can be a proc, in which case it will be invoked on each request.
48
- def initialize(app, type, param)
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)
49
17
  @type = type
50
- @param = param
18
+ @params = params
51
19
  super(app)
52
20
  end
53
21
 
@@ -55,8 +23,32 @@ module Faraday
55
23
  def on_request(env)
56
24
  return if env.request_headers[KEY]
57
25
 
58
- env.request_headers[KEY] = self.class.header(@type, @param)
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
59
49
  end
60
50
  end
61
51
  end
62
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)
@@ -13,7 +13,7 @@ module Faraday
13
13
  # Doesn't try to encode bodies that already are in string form.
14
14
  class Json < Middleware
15
15
  MIME_TYPE = 'application/json'
16
- MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}.freeze
16
+ MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}
17
17
 
18
18
  def on_request(env)
19
19
  match_content_type(env) do |data|
@@ -24,7 +24,13 @@ module Faraday
24
24
  private
25
25
 
26
26
  def encode(data)
27
- ::JSON.generate(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
28
34
  end
29
35
 
30
36
  def match_content_type(env)
@@ -40,7 +46,16 @@ module Faraday
40
46
  end
41
47
 
42
48
  def body?(env)
43
- (body = env[:body]) && !(body.respond_to?(:to_str) && body.empty?)
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
44
59
  end
45
60
 
46
61
  def request_type(env)
@@ -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,31 +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
- 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'],
48
- json: [:Json, 'json']
30
+ alias_method :member_get, :[]
31
+ private :member_get
32
+ alias_method :member_set, :[]=
33
+ private :member_set
49
34
 
50
35
  # @param request_method [String]
51
36
  # @yield [request] for block customization, if block given
@@ -57,13 +42,7 @@ module Faraday
57
42
  end
58
43
  end
59
44
 
60
- def method
61
- http_method
62
- end
63
-
64
- extend Faraday::Deprecate
65
- deprecate :method, :http_method, '2.0'
66
-
45
+ remove_method :params=
67
46
  # Replace params, preserving the existing hash type.
68
47
  #
69
48
  # @param hash [Hash] new params
@@ -71,10 +50,11 @@ module Faraday
71
50
  if params
72
51
  params.replace hash
73
52
  else
74
- super
53
+ member_set(:params, hash)
75
54
  end
76
55
  end
77
56
 
57
+ remove_method :headers=
78
58
  # Replace request headers, preserving the existing hash type.
79
59
  #
80
60
  # @param hash [Hash] new headers
@@ -82,7 +62,7 @@ module Faraday
82
62
  if headers
83
63
  headers.replace hash
84
64
  else
85
- super
65
+ member_set(:headers, hash)
86
66
  end
87
67
  end
88
68
 
@@ -152,3 +132,8 @@ module Faraday
152
132
  end
153
133
  end
154
134
  end
135
+
136
+ require 'faraday/request/authorization'
137
+ require 'faraday/request/instrumentation'
138
+ require 'faraday/request/json'
139
+ require 'faraday/request/url_encoded'
@@ -6,11 +6,13 @@ module Faraday
6
6
  class Response
7
7
  # Parse response bodies as JSON.
8
8
  class Json < Middleware
9
- def initialize(app = nil, options = {})
9
+ def initialize(app = nil, parser_options: nil, content_type: /\bjson$/, preserve_raw: false)
10
10
  super(app)
11
- @parser_options = options[:parser_options]
12
- @content_types = Array(options[:content_type] || /\bjson$/)
13
- @preserve_raw = options[:preserve_raw]
11
+ @parser_options = parser_options
12
+ @content_types = Array(content_type)
13
+ @preserve_raw = preserve_raw
14
+
15
+ process_parser_options
14
16
  end
15
17
 
16
18
  def on_complete(env)
@@ -27,7 +29,11 @@ module Faraday
27
29
  end
28
30
 
29
31
  def parse(body)
30
- ::JSON.parse(body, @parser_options || {}) unless body.strip.empty?
32
+ return if body.strip.empty?
33
+
34
+ decoder, method_name = @decoder_options
35
+
36
+ decoder.public_send(method_name, body, @parser_options || {})
31
37
  end
32
38
 
33
39
  def parse_response?(env)
@@ -47,6 +53,20 @@ module Faraday
47
53
  type = type.split(';', 2).first if type.index(';')
48
54
  type
49
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) # rubocop:disable Lint/RedundantSafeNavigation
64
+ # In some versions of Rails, `nil` responds to `load` - hence the safe navigation check above
65
+ [@decoder_options, :load]
66
+ else
67
+ [::JSON, :parse]
68
+ end
69
+ end
50
70
  end
51
71
  end
52
72
  end
@@ -10,11 +10,13 @@ module Faraday
10
10
  # lifecycle to a given Logger object. By default, this logs to STDOUT. See
11
11
  # Faraday::Logging::Formatter to see specifically what is logged.
12
12
  class Logger < Middleware
13
+ DEFAULT_OPTIONS = { formatter: Logging::Formatter }.merge(Logging::Formatter::DEFAULT_OPTIONS).freeze
14
+
13
15
  def initialize(app, logger = nil, options = {})
14
- super(app)
16
+ super(app, options)
15
17
  logger ||= ::Logger.new($stdout)
16
- formatter_class = options.delete(:formatter) || Logging::Formatter
17
- @formatter = formatter_class.new(logger: logger, options: options)
18
+ formatter_class = @options.delete(:formatter)
19
+ @formatter = formatter_class.new(logger: logger, options: @options)
18
20
  yield @formatter if block_given?
19
21
  end
20
22
 
@@ -26,6 +28,12 @@ module Faraday
26
28
  def on_complete(env)
27
29
  @formatter.response(env)
28
30
  end
31
+
32
+ def on_error(exc)
33
+ @formatter.exception(exc) if @formatter.respond_to?(:exception)
34
+ end
29
35
  end
30
36
  end
31
37
  end
38
+
39
+ Faraday::Response.register_middleware(logger: Faraday::Response::Logger)
@@ -6,28 +6,32 @@ 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
+ ClientErrorStatusesWithCustomExceptions = {
12
+ 400 => Faraday::BadRequestError,
13
+ 401 => Faraday::UnauthorizedError,
14
+ 403 => Faraday::ForbiddenError,
15
+ 404 => Faraday::ResourceNotFound,
16
+ 408 => Faraday::RequestTimeoutError,
17
+ 409 => Faraday::ConflictError,
18
+ 422 => Faraday::UnprocessableContentError,
19
+ 429 => Faraday::TooManyRequestsError
20
+ }.freeze
11
21
  # rubocop:enable Naming/ConstantName
12
22
 
23
+ DEFAULT_OPTIONS = { include_request: true, allowed_statuses: [] }.freeze
24
+
13
25
  def on_complete(env)
26
+ return if Array(options[:allowed_statuses]).include?(env[:status])
27
+
14
28
  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)
29
+ when *ClientErrorStatusesWithCustomExceptions.keys
30
+ raise ClientErrorStatusesWithCustomExceptions[env[:status]], response_values(env)
23
31
  when 407
24
32
  # mimic the behavior that we get with proxy requests with HTTPS
25
33
  msg = %(407 "Proxy Authentication Required")
26
34
  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
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[:include_request]
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,26 +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
- # Override this to modify the environment after the response has finished.
11
- # Calls the `parse` method if defined
12
- # `parse` method can be defined as private, public and protected
13
- def on_complete(env)
14
- return unless respond_to?(:parse, true) && env.parse_body?
15
-
16
- env.body = parse(env.body)
17
- end
18
- end
19
-
20
8
  extend Forwardable
21
9
  extend MiddlewareRegistry
22
10
 
23
- register_middleware File.expand_path('response', __dir__),
24
- raise_error: [:RaiseError, 'raise_error'],
25
- logger: [:Logger, 'logger'],
26
- json: [:Json, 'json']
27
-
28
11
  def initialize(env = nil)
29
12
  @env = Env.from(env) if env
30
13
  @on_complete_callbacks = []
@@ -50,15 +33,19 @@ module Faraday
50
33
  finished? ? env.body : nil
51
34
  end
52
35
 
36
+ def url
37
+ finished? ? env.url : nil
38
+ end
39
+
53
40
  def finished?
54
41
  !!env
55
42
  end
56
43
 
57
44
  def on_complete(&block)
58
- if !finished?
59
- @on_complete_callbacks << block
60
- else
45
+ if finished?
61
46
  yield(env)
47
+ else
48
+ @on_complete_callbacks << block
62
49
  end
63
50
  self
64
51
  end
@@ -77,8 +64,9 @@ module Faraday
77
64
 
78
65
  def to_hash
79
66
  {
80
- status: env.status, body: env.body,
81
- response_headers: env.response_headers
67
+ status: status, body: body,
68
+ response_headers: headers,
69
+ url: url
82
70
  }
83
71
  end
84
72
 
@@ -101,3 +89,7 @@ module Faraday
101
89
  end
102
90
  end
103
91
  end
92
+
93
+ require 'faraday/response/json'
94
+ require 'faraday/response/logger'
95
+ require 'faraday/response/raise_error'
@@ -62,10 +62,10 @@ module Faraday
62
62
  super(key, val)
63
63
  end
64
64
 
65
- def fetch(key, *args, &block)
65
+ def fetch(key, ...)
66
66
  key = KeyMap[key]
67
67
  key = @names.fetch(key.downcase, key)
68
- super(key, *args, &block)
68
+ super(key, ...)
69
69
  end
70
70
 
71
71
  def delete(key)
@@ -77,6 +77,12 @@ module Faraday
77
77
  super(key)
78
78
  end
79
79
 
80
+ def dig(key, *rest)
81
+ key = KeyMap[key]
82
+ key = @names.fetch(key.downcase, key)
83
+ super(key, *rest)
84
+ end
85
+
80
86
  def include?(key)
81
87
  @names.include? key.downcase
82
88
  end
@@ -111,7 +117,7 @@ module Faraday
111
117
  def parse(header_string)
112
118
  return unless header_string && !header_string.empty?
113
119
 
114
- headers = header_string.split(/\r\n/)
120
+ headers = header_string.split("\r\n")
115
121
 
116
122
  # Find the last set of response headers.
117
123
  start_index = headers.rindex { |x| x.start_with?('HTTP/') } || 0
@@ -132,7 +138,12 @@ module Faraday
132
138
 
133
139
  # Join multiple values with a comma.
134
140
  def add_parsed(key, value)
135
- self[key] ? self[key] << ', ' << value : self[key] = value
141
+ if key?(key)
142
+ self[key] = self[key].to_s
143
+ self[key] << ', ' << value
144
+ else
145
+ self[key] = value
146
+ end
136
147
  end
137
148
  end
138
149
  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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Faraday
4
- VERSION = '1.10.4'
4
+ VERSION = '2.14.0'
5
5
  end