faraday 0.17.3 → 1.9.3

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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +156 -8
  3. data/LICENSE.md +1 -1
  4. data/README.md +16 -358
  5. data/Rakefile +1 -7
  6. data/examples/client_spec.rb +97 -0
  7. data/examples/client_test.rb +118 -0
  8. data/lib/faraday/adapter/test.rb +118 -69
  9. data/lib/faraday/adapter/typhoeus.rb +4 -1
  10. data/lib/faraday/adapter.rb +72 -22
  11. data/lib/faraday/adapter_registry.rb +30 -0
  12. data/lib/faraday/autoload.rb +39 -36
  13. data/lib/faraday/connection.rb +343 -185
  14. data/lib/faraday/dependency_loader.rb +37 -0
  15. data/lib/faraday/encoders/flat_params_encoder.rb +105 -0
  16. data/lib/faraday/encoders/nested_params_encoder.rb +176 -0
  17. data/lib/faraday/error.rb +28 -40
  18. data/lib/faraday/logging/formatter.rb +105 -0
  19. data/lib/faraday/methods.rb +6 -0
  20. data/lib/faraday/middleware.rb +19 -25
  21. data/lib/faraday/middleware_registry.rb +129 -0
  22. data/lib/faraday/options/connection_options.rb +22 -0
  23. data/lib/faraday/options/env.rb +181 -0
  24. data/lib/faraday/options/proxy_options.rb +32 -0
  25. data/lib/faraday/options/request_options.rb +22 -0
  26. data/lib/faraday/options/ssl_options.rb +59 -0
  27. data/lib/faraday/options.rb +36 -191
  28. data/lib/faraday/parameters.rb +4 -197
  29. data/lib/faraday/rack_builder.rb +76 -64
  30. data/lib/faraday/request/authorization.rb +51 -30
  31. data/lib/faraday/request/basic_authentication.rb +14 -7
  32. data/lib/faraday/request/instrumentation.rb +45 -27
  33. data/lib/faraday/request/token_authentication.rb +15 -10
  34. data/lib/faraday/request/url_encoded.rb +43 -23
  35. data/lib/faraday/request.rb +84 -44
  36. data/lib/faraday/response/logger.rb +22 -69
  37. data/lib/faraday/response/raise_error.rb +49 -18
  38. data/lib/faraday/response.rb +24 -20
  39. data/lib/faraday/utils/headers.rb +139 -0
  40. data/lib/faraday/utils/params_hash.rb +61 -0
  41. data/lib/faraday/utils.rb +38 -247
  42. data/lib/faraday/version.rb +5 -0
  43. data/lib/faraday.rb +133 -189
  44. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  45. data/spec/faraday/adapter/em_http_spec.rb +49 -0
  46. data/spec/faraday/adapter/em_synchrony_spec.rb +18 -0
  47. data/spec/faraday/adapter/excon_spec.rb +49 -0
  48. data/spec/faraday/adapter/httpclient_spec.rb +73 -0
  49. data/spec/faraday/adapter/net_http_spec.rb +64 -0
  50. data/spec/faraday/adapter/patron_spec.rb +18 -0
  51. data/spec/faraday/adapter/rack_spec.rb +8 -0
  52. data/spec/faraday/adapter/test_spec.rb +377 -0
  53. data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
  54. data/spec/faraday/adapter_registry_spec.rb +28 -0
  55. data/spec/faraday/adapter_spec.rb +55 -0
  56. data/spec/faraday/composite_read_io_spec.rb +80 -0
  57. data/spec/faraday/connection_spec.rb +736 -0
  58. data/spec/faraday/error_spec.rb +12 -54
  59. data/spec/faraday/middleware_spec.rb +52 -0
  60. data/spec/faraday/options/env_spec.rb +70 -0
  61. data/spec/faraday/options/options_spec.rb +297 -0
  62. data/spec/faraday/options/proxy_options_spec.rb +44 -0
  63. data/spec/faraday/options/request_options_spec.rb +19 -0
  64. data/spec/faraday/params_encoders/flat_spec.rb +42 -0
  65. data/spec/faraday/params_encoders/nested_spec.rb +142 -0
  66. data/spec/faraday/rack_builder_spec.rb +345 -0
  67. data/spec/faraday/request/authorization_spec.rb +96 -0
  68. data/spec/faraday/request/instrumentation_spec.rb +76 -0
  69. data/spec/faraday/request/url_encoded_spec.rb +83 -0
  70. data/spec/faraday/request_spec.rb +120 -0
  71. data/spec/faraday/response/logger_spec.rb +220 -0
  72. data/spec/faraday/response/middleware_spec.rb +68 -0
  73. data/spec/faraday/response/raise_error_spec.rb +78 -15
  74. data/spec/faraday/response_spec.rb +75 -0
  75. data/spec/faraday/utils/headers_spec.rb +82 -0
  76. data/spec/faraday/utils_spec.rb +56 -0
  77. data/spec/faraday_spec.rb +37 -0
  78. data/spec/spec_helper.rb +63 -36
  79. data/spec/support/disabling_stub.rb +14 -0
  80. data/spec/support/fake_safe_buffer.rb +15 -0
  81. data/spec/support/helper_methods.rb +133 -0
  82. data/spec/support/shared_examples/adapter.rb +105 -0
  83. data/spec/support/shared_examples/params_encoder.rb +18 -0
  84. data/spec/support/shared_examples/request_method.rb +262 -0
  85. data/spec/support/streaming_response_checker.rb +35 -0
  86. data/spec/support/webmock_rack_app.rb +68 -0
  87. metadata +206 -58
  88. data/lib/faraday/adapter/em_http.rb +0 -243
  89. data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -56
  90. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -66
  91. data/lib/faraday/adapter/em_synchrony.rb +0 -106
  92. data/lib/faraday/adapter/excon.rb +0 -82
  93. data/lib/faraday/adapter/httpclient.rb +0 -128
  94. data/lib/faraday/adapter/net_http.rb +0 -152
  95. data/lib/faraday/adapter/net_http_persistent.rb +0 -68
  96. data/lib/faraday/adapter/patron.rb +0 -95
  97. data/lib/faraday/adapter/rack.rb +0 -58
  98. data/lib/faraday/deprecate.rb +0 -107
  99. data/lib/faraday/request/multipart.rb +0 -68
  100. data/lib/faraday/request/retry.rb +0 -213
  101. data/lib/faraday/upload_io.rb +0 -67
  102. data/spec/faraday/deprecate_spec.rb +0 -69
  103. data/test/adapters/default_test.rb +0 -14
  104. data/test/adapters/em_http_test.rb +0 -30
  105. data/test/adapters/em_synchrony_test.rb +0 -32
  106. data/test/adapters/excon_test.rb +0 -30
  107. data/test/adapters/httpclient_test.rb +0 -34
  108. data/test/adapters/integration.rb +0 -263
  109. data/test/adapters/logger_test.rb +0 -136
  110. data/test/adapters/net_http_persistent_test.rb +0 -114
  111. data/test/adapters/net_http_test.rb +0 -79
  112. data/test/adapters/patron_test.rb +0 -40
  113. data/test/adapters/rack_test.rb +0 -38
  114. data/test/adapters/test_middleware_test.rb +0 -157
  115. data/test/adapters/typhoeus_test.rb +0 -38
  116. data/test/authentication_middleware_test.rb +0 -65
  117. data/test/composite_read_io_test.rb +0 -109
  118. data/test/connection_test.rb +0 -738
  119. data/test/env_test.rb +0 -268
  120. data/test/helper.rb +0 -75
  121. data/test/live_server.rb +0 -67
  122. data/test/middleware/instrumentation_test.rb +0 -88
  123. data/test/middleware/retry_test.rb +0 -282
  124. data/test/middleware_stack_test.rb +0 -260
  125. data/test/multibyte.txt +0 -1
  126. data/test/options_test.rb +0 -333
  127. data/test/parameters_test.rb +0 -157
  128. data/test/request_middleware_test.rb +0 -126
  129. data/test/response_middleware_test.rb +0 -72
  130. data/test/strawberry.rb +0 -2
  131. data/test/utils_test.rb +0 -98
@@ -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,25 +12,61 @@ module Faraday
9
12
  # req.body = 'abc'
10
13
  # end
11
14
  #
12
- class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
15
+ # @!attribute http_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(
30
+ :http_method, :path, :params, :headers, :body, :options
31
+ )
32
+ # rubocop:enable Style/StructInheritance
33
+
13
34
  extend MiddlewareRegistry
14
35
 
15
- register_middleware File.expand_path('../request', __FILE__),
16
- :url_encoded => [:UrlEncoded, 'url_encoded'],
17
- :multipart => [:Multipart, 'multipart'],
18
- :retry => [:Retry, 'retry'],
19
- :authorization => [:Authorization, 'authorization'],
20
- :basic_auth => [:BasicAuthentication, 'basic_authentication'],
21
- :token_auth => [:TokenAuthentication, 'token_authentication'],
22
- :instrumentation => [:Instrumentation, 'instrumentation']
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']
23
48
 
49
+ # @param request_method [String]
50
+ # @yield [request] for block customization, if block given
51
+ # @yieldparam request [Request]
52
+ # @return [Request]
24
53
  def self.create(request_method)
25
54
  new(request_method).tap do |request|
26
55
  yield(request) if block_given?
27
56
  end
28
57
  end
29
58
 
30
- # Public: Replace params, preserving the existing hash type
59
+ def method
60
+ warn <<~TEXT
61
+ WARNING: `Faraday::Request##{__method__}` is deprecated; use `#http_method` instead. It will be removed in or after version 2.0.
62
+ `Faraday::Request##{__method__}` called from #{caller_locations(1..1).first}
63
+ TEXT
64
+ http_method
65
+ end
66
+
67
+ # Replace params, preserving the existing hash type.
68
+ #
69
+ # @param hash [Hash] new params
31
70
  def params=(hash)
32
71
  if params
33
72
  params.replace hash
@@ -36,7 +75,9 @@ module Faraday
36
75
  end
37
76
  end
38
77
 
39
- # Public: Replace request headers, preserving the existing hash type
78
+ # Replace request headers, preserving the existing hash type.
79
+ #
80
+ # @param hash [Hash] new headers
40
81
  def headers=(hash)
41
82
  if headers
42
83
  headers.replace hash
@@ -45,9 +86,14 @@ module Faraday
45
86
  end
46
87
  end
47
88
 
89
+ # Update path and params.
90
+ #
91
+ # @param path [URI, String]
92
+ # @param params [Hash, nil]
93
+ # @return [void]
48
94
  def url(path, params = nil)
49
95
  if path.respond_to? :query
50
- if query = path.query
96
+ if (query = path.query)
51
97
  path = path.dup
52
98
  path.query = nil
53
99
  end
@@ -61,54 +107,48 @@ module Faraday
61
107
  self.params.update(params) if params
62
108
  end
63
109
 
110
+ # @param key [Object] key to look up in headers
111
+ # @return [Object] value of the given header name
64
112
  def [](key)
65
113
  headers[key]
66
114
  end
67
115
 
116
+ # @param key [Object] key of header to write
117
+ # @param value [Object] value of header
68
118
  def []=(key, value)
69
119
  headers[key] = value
70
120
  end
71
121
 
122
+ # Marshal serialization support.
123
+ #
124
+ # @return [Hash] the hash ready to be serialized in Marshal.
72
125
  def marshal_dump
73
126
  {
74
- :method => method,
75
- :body => body,
76
- :headers => headers,
77
- :path => path,
78
- :params => params,
79
- :options => options
127
+ http_method: http_method,
128
+ body: body,
129
+ headers: headers,
130
+ path: path,
131
+ params: params,
132
+ options: options
80
133
  }
81
134
  end
82
135
 
136
+ # Marshal serialization support.
137
+ # Restores the instance variables according to the +serialised+.
138
+ # @param serialised [Hash] the serialised object.
83
139
  def marshal_load(serialised)
84
- self.method = serialised[:method]
85
- self.body = serialised[:body]
86
- self.headers = serialised[:headers]
87
- self.path = serialised[:path]
88
- self.params = serialised[:params]
89
- self.options = serialised[:options]
140
+ self.http_method = serialised[:http_method]
141
+ self.body = serialised[:body]
142
+ self.headers = serialised[:headers]
143
+ self.path = serialised[:path]
144
+ self.params = serialised[:params]
145
+ self.options = serialised[:options]
90
146
  end
91
147
 
92
- # ENV Keys
93
- # :method - a symbolized request method (:get, :post)
94
- # :body - the request body that will eventually be converted to a string.
95
- # :url - URI instance for the current request.
96
- # :status - HTTP response status code
97
- # :request_headers - hash of HTTP Headers to be sent to the server
98
- # :response_headers - Hash of HTTP headers from the server
99
- # :parallel_manager - sent if the connection is in parallel mode
100
- # :request - Hash of options for configuring the request.
101
- # :timeout - open/read timeout Integer in seconds
102
- # :open_timeout - read timeout Integer in seconds
103
- # :proxy - Hash of proxy options
104
- # :uri - Proxy Server URI
105
- # :user - Proxy server username
106
- # :password - Proxy server password
107
- # :ssl - Hash of options for configuring SSL requests.
148
+ # @return [Env] the Env for this Request
108
149
  def to_env(connection)
109
- Env.new(method, body, connection.build_exclusive_url(path, params),
110
- options, headers, connection.ssl, connection.parallel_manager)
150
+ Env.new(http_method, body, connection.build_exclusive_url(path, params),
151
+ options, headers, connection.ssl, connection.parallel_manager)
111
152
  end
112
153
  end
113
154
  end
114
-
@@ -1,80 +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
- DEFAULT_OPTIONS = { :headers => true, :bodies => false }
8
-
9
- def initialize(app, logger = nil, options = {})
10
- super(app)
11
- @logger = logger || begin
12
- require 'logger'
13
- ::Logger.new($stdout)
14
- end
15
- @filter = []
16
- @options = DEFAULT_OPTIONS.merge(options)
17
- yield self if block_given?
18
- end
19
-
20
- def_delegators :@logger, :debug, :info, :warn, :error, :fatal
21
-
22
- def call(env)
23
- info('request') { "#{env.method.upcase} #{apply_filters(env.url.to_s)}" }
24
- debug('request') { apply_filters( dump_headers env.request_headers ) } if log_headers?(:request)
25
- debug('request') { apply_filters( dump_body(env[:body]) ) } if env[:body] && log_body?(:request)
26
- super
27
- end
28
-
29
- def on_complete(env)
30
- info('response') { "Status #{env.status.to_s}" }
31
- debug('response') { apply_filters( dump_headers env.response_headers ) } if log_headers?(:response)
32
- debug('response') { apply_filters( dump_body env[:body] ) } if env[:body] && log_body?(:response)
33
- end
34
-
35
- def filter(filter_word, filter_replacement)
36
- @filter.push([ filter_word, filter_replacement ])
37
- end
38
-
39
- private
40
-
41
- def dump_headers(headers)
42
- headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
43
- end
44
-
45
- def dump_body(body)
46
- if body.respond_to?(:to_str)
47
- body.to_str
48
- else
49
- pretty_inspect(body)
50
- end
51
- end
52
-
53
- def pretty_inspect(body)
54
- require 'pp' unless body.respond_to?(:pretty_inspect)
55
- body.pretty_inspect
56
- end
57
-
58
- def log_headers?(type)
59
- case @options[:headers]
60
- when Hash then @options[:headers][type]
61
- else @options[:headers]
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?
62
21
  end
63
- end
64
22
 
65
- def log_body?(type)
66
- case @options[:bodies]
67
- when Hash then @options[:bodies][type]
68
- else @options[:bodies]
23
+ def call(env)
24
+ @formatter.request(env)
25
+ super
69
26
  end
70
- end
71
27
 
72
- def apply_filters(output)
73
- @filter.each do |pattern, replacement|
74
- output = output.to_s.gsub(pattern, replacement)
28
+ def on_complete(env)
29
+ @formatter.response(env)
75
30
  end
76
- output
77
31
  end
78
-
79
32
  end
80
33
  end
@@ -1,25 +1,56 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
- class Response::RaiseError < Response::Middleware
3
- ClientErrorStatuses = 400...600
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
4
12
 
5
- def on_complete(env)
6
- case env[:status]
7
- when 404
8
- raise Faraday::ResourceNotFound, response_values(env)
9
- when 407
10
- # mimic the behavior that we get with proxy requests with HTTPS
11
- raise Faraday::ConnectionFailed.new(
12
- %{407 "Proxy Authentication Required "},
13
- response_values(env))
14
- when ClientErrorStatuses
15
- raise Faraday::ClientError, response_values(env)
16
- when nil
17
- raise Faraday::NilStatusError, response_values(env)
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
18
38
  end
19
- end
20
39
 
21
- def response_values(env)
22
- {:status => env.status, :headers => env.response_headers, :body => env.body}
40
+ def response_values(env)
41
+ {
42
+ status: env.status,
43
+ headers: env.response_headers,
44
+ body: env.body,
45
+ request: {
46
+ method: env.method,
47
+ url_path: env.url.path,
48
+ params: env.params,
49
+ headers: env.request_headers,
50
+ body: env.request_body
51
+ }
52
+ }
53
+ end
23
54
  end
24
55
  end
25
56
  end
@@ -1,28 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
 
3
5
  module Faraday
6
+ # Response represents an HTTP response from making an HTTP request.
4
7
  class Response
5
8
  # Used for simple response middleware.
6
9
  class Middleware < Faraday::Middleware
7
- def call(env)
8
- @app.call(env).on_complete do |environment|
9
- on_complete(environment)
10
- end
11
- end
12
-
13
10
  # Override this to modify the environment after the response has finished.
14
11
  # Calls the `parse` method if defined
12
+ # `parse` method can be defined as private, public and protected
15
13
  def on_complete(env)
16
- env.body = parse(env.body) if respond_to?(:parse) && env.parse_body?
14
+ return unless respond_to?(:parse, true) && env.parse_body?
15
+
16
+ env.body = parse(env.body)
17
17
  end
18
18
  end
19
19
 
20
20
  extend Forwardable
21
21
  extend MiddlewareRegistry
22
22
 
23
- register_middleware File.expand_path('../response', __FILE__),
24
- :raise_error => [:RaiseError, 'raise_error'],
25
- :logger => [:Logger, 'logger']
23
+ register_middleware File.expand_path('response', __dir__),
24
+ raise_error: [:RaiseError, 'raise_error'],
25
+ logger: [:Logger, 'logger']
26
26
 
27
27
  def initialize(env = nil)
28
28
  @env = Env.from(env) if env
@@ -31,8 +31,6 @@ module Faraday
31
31
 
32
32
  attr_reader :env
33
33
 
34
- def_delegators :env, :to_hash
35
-
36
34
  def status
37
35
  finished? ? env.status : nil
38
36
  end
@@ -60,26 +58,31 @@ module Faraday
60
58
  else
61
59
  yield(env)
62
60
  end
63
- return self
61
+ self
64
62
  end
65
63
 
66
64
  def finish(env)
67
- raise "response already finished" if finished?
65
+ raise 'response already finished' if finished?
66
+
68
67
  @env = env.is_a?(Env) ? env : Env.from(env)
69
68
  @on_complete_callbacks.each { |callback| callback.call(@env) }
70
- return self
69
+ self
71
70
  end
72
71
 
73
72
  def success?
74
73
  finished? && env.success?
75
74
  end
76
75
 
76
+ def to_hash
77
+ {
78
+ status: env.status, body: env.body,
79
+ response_headers: env.response_headers
80
+ }
81
+ end
82
+
77
83
  # because @on_complete_callbacks cannot be marshalled
78
84
  def marshal_dump
79
- !finished? ? nil : {
80
- :status => @env.status, :body => @env.body,
81
- :response_headers => @env.response_headers
82
- }
85
+ finished? ? to_hash : nil
83
86
  end
84
87
 
85
88
  def marshal_load(env)
@@ -90,8 +93,9 @@ module Faraday
90
93
  # Useful for applying request params after restoring a marshalled Response.
91
94
  def apply_request(request_env)
92
95
  raise "response didn't finish yet" unless finished?
96
+
93
97
  @env = Env.from(request_env).update(@env)
94
- return self
98
+ self
95
99
  end
96
100
  end
97
101
  end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ module Utils
5
+ # A case-insensitive Hash that preserves the original case of a header
6
+ # when set.
7
+ #
8
+ # Adapted from Rack::Utils::HeaderHash
9
+ class Headers < ::Hash
10
+ def self.from(value)
11
+ new(value)
12
+ end
13
+
14
+ def self.allocate
15
+ new_self = super
16
+ new_self.initialize_names
17
+ new_self
18
+ end
19
+
20
+ def initialize(hash = nil)
21
+ super()
22
+ @names = {}
23
+ update(hash || {})
24
+ end
25
+
26
+ def initialize_names
27
+ @names = {}
28
+ end
29
+
30
+ # on dup/clone, we need to duplicate @names hash
31
+ def initialize_copy(other)
32
+ super
33
+ @names = other.names.dup
34
+ end
35
+
36
+ # need to synchronize concurrent writes to the shared KeyMap
37
+ keymap_mutex = Mutex.new
38
+
39
+ # symbol -> string mapper + cache
40
+ KeyMap = Hash.new do |map, key|
41
+ value = if key.respond_to?(:to_str)
42
+ key
43
+ else
44
+ key.to_s.split('_') # user_agent: %w(user agent)
45
+ .each(&:capitalize!) # => %w(User Agent)
46
+ .join('-') # => "User-Agent"
47
+ end
48
+ keymap_mutex.synchronize { map[key] = value }
49
+ end
50
+ KeyMap[:etag] = 'ETag'
51
+
52
+ def [](key)
53
+ key = KeyMap[key]
54
+ super(key) || super(@names[key.downcase])
55
+ end
56
+
57
+ def []=(key, val)
58
+ key = KeyMap[key]
59
+ key = (@names[key.downcase] ||= key)
60
+ # join multiple values with a comma
61
+ val = val.to_ary.join(', ') if val.respond_to?(:to_ary)
62
+ super(key, val)
63
+ end
64
+
65
+ def fetch(key, *args, &block)
66
+ key = KeyMap[key]
67
+ key = @names.fetch(key.downcase, key)
68
+ super(key, *args, &block)
69
+ end
70
+
71
+ def delete(key)
72
+ key = KeyMap[key]
73
+ key = @names[key.downcase]
74
+ return unless key
75
+
76
+ @names.delete key.downcase
77
+ super(key)
78
+ end
79
+
80
+ def include?(key)
81
+ @names.include? key.downcase
82
+ end
83
+
84
+ alias has_key? include?
85
+ alias member? include?
86
+ alias key? include?
87
+
88
+ def merge!(other)
89
+ other.each { |k, v| self[k] = v }
90
+ self
91
+ end
92
+
93
+ alias update merge!
94
+
95
+ def merge(other)
96
+ hash = dup
97
+ hash.merge! other
98
+ end
99
+
100
+ def replace(other)
101
+ clear
102
+ @names.clear
103
+ update other
104
+ self
105
+ end
106
+
107
+ def to_hash
108
+ {}.update(self)
109
+ end
110
+
111
+ def parse(header_string)
112
+ return unless header_string && !header_string.empty?
113
+
114
+ headers = header_string.split(/\r\n/)
115
+
116
+ # Find the last set of response headers.
117
+ start_index = headers.rindex { |x| x.start_with?('HTTP/') } || 0
118
+ last_response = headers.slice(start_index, headers.size)
119
+
120
+ last_response
121
+ .tap { |a| a.shift if a.first.start_with?('HTTP/') }
122
+ .map { |h| h.split(/:\s*/, 2) } # split key and value
123
+ .reject { |p| p[0].nil? } # ignore blank lines
124
+ .each { |key, value| add_parsed(key, value) }
125
+ end
126
+
127
+ protected
128
+
129
+ attr_reader :names
130
+
131
+ private
132
+
133
+ # Join multiple values with a comma.
134
+ def add_parsed(key, value)
135
+ self[key] ? self[key] << ', ' << value : self[key] = value
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ module Utils
5
+ # A hash with stringified keys.
6
+ class ParamsHash < Hash
7
+ def [](key)
8
+ super(convert_key(key))
9
+ end
10
+
11
+ def []=(key, value)
12
+ super(convert_key(key), value)
13
+ end
14
+
15
+ def delete(key)
16
+ super(convert_key(key))
17
+ end
18
+
19
+ def include?(key)
20
+ super(convert_key(key))
21
+ end
22
+
23
+ alias has_key? include?
24
+ alias member? include?
25
+ alias key? include?
26
+
27
+ def update(params)
28
+ params.each do |key, value|
29
+ self[key] = value
30
+ end
31
+ self
32
+ end
33
+ alias merge! update
34
+
35
+ def merge(params)
36
+ dup.update(params)
37
+ end
38
+
39
+ def replace(other)
40
+ clear
41
+ update(other)
42
+ end
43
+
44
+ def merge_query(query, encoder = nil)
45
+ return self unless query && !query.empty?
46
+
47
+ update((encoder || Utils.default_params_encoder).decode(query))
48
+ end
49
+
50
+ def to_query(encoder = nil)
51
+ (encoder || Utils.default_params_encoder).encode(self)
52
+ end
53
+
54
+ private
55
+
56
+ def convert_key(key)
57
+ key.to_s
58
+ end
59
+ end
60
+ end
61
+ end